Files

1 line
4.9 MiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "aghontpi/aghontpi.github.io", "files": {"index.xml": {"15a554dd0ff12e651a15843efa8195fddced09b7": {"t": 1785747149.229267, "n": 161063, "h": "85a85022be21a2b4", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Thu, 18 Jun 2026 21:50:43 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>June 2026 - Deepseek API For Performance &amp; Cost</title><link>//bluepie.in/2026/06/june-2026/</link><pubDate>Thu, 18 Jun 2026 21:50:43 +0000</pubDate><guid>//bluepie.in/2026/06/june-2026/</guid><description>\n&lt;blockquote&gt;\n&lt;p&gt;(following post has been updated 5th of july, 2026)&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;intro&#34;&gt;Intro&lt;/h3&gt;\n&lt;p&gt;A shift in LLM/AI usage: GitHub Copilot changed its user pricing. Microsoft changed the pricing. Nvidia &amp;amp; AMD are coming up with new laptops with 128 or more GB of VRAM to run agents and LLMs locally! But the prices of the chips are not going down; at the time of writing, the Nvidia RTX 5070 16GB is 65k INR.&lt;/p&gt;\n&lt;p&gt;The company that I work for at the time of writing this broke the Microsoft GitHub Copilot tie-up and looked for other vendors. They did it so fast: they sent a company-wide email stating it would be terminated immediately for the FTE (own employees) developers but will be kept only for the vendors, and asked users to utilize and adopt Claude Code. So we started using that, but a lot of people just use it for the sake of it to finish the job. At this point, there is no auto mode in the Claude version they allow inside the organization; people just set the model to high and burn through the tokens. It&amp;rsquo;s been 2 days and people went ham on it, and already 25% of the team budget is gone. We can check out individual budgets and the team&amp;rsquo;s budget at the usage metrics.&lt;/p&gt;\n&lt;p&gt;For me, I got around 300 dollars of AI credits at my disposal: $100 for Claude and around 200 for Cursor. To think that is around 28k INR—the salary I got at the time of starting my career when I left college. Back then, was it considered good money? Things changed really fast and got pricey, huh? These token budgets are arbitrary values set by companies on their own terms and bullshit logic. We are paying money and allocating budget for thin air—tokens? A decade ago, people would laugh at the idea! That would be 2016. I remember 2016; it was a different time: peaceful, chaotic, it had its charms.&lt;/p&gt;\n&lt;h2 id=&#34;deepseek-api-for-the-win&#34;&gt;Deepseek api For The Win&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using the DeepSeek API directly for the entire month of June 2026. It&amp;rsquo;s been great; they offer cheap prices for the latest model they have (at the point of writing this in June/July 2026 (this line was edited on July 5th, 2026, 1:08 AM)). Here is a comparison of the API pricing per 1 million tokens (as of July 2026):&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th align=&#34;left&#34;&gt;Provider&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Model&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Input Price (Cache Miss)&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Input Price (Cache Hit)&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;DeepSeek&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;deepseek-v4-flash&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.14&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.0028&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.28&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;deepseek-v4-pro&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.435&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.003625&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.87&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Google&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Gemini 2.5 Flash-Lite&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.10&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;—&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.40&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Gemini 3.5 Flash&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$1.50&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;—&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$9.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Gemini 3.1 Pro Preview&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$2.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;—&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$12.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Anthropic&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Claude Haiku 4.5&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$1.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.10 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$5.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Claude Sonnet 5&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$3.00*&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.30 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$15.00*&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Claude Opus 4.8&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$5.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.50 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$25.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;OpenAI&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;GPT-5.4 Nano&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.20&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.02 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$1.25&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;GPT-5.4 (Production)&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$2.50&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.25 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$15.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;GPT-5.5 (Flagship)&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$5.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.50 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$30.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;em&gt;*Note: Claude Sonnet 5 features introductory pricing of $2.00/$10.00 per million tokens through August 31, 2026. For verification, check the official docs: &lt;a href=&#34;https://platform.deepseek.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;DeepSeek Pricing&lt;/a&gt;, &lt;a href=&#34;https://aistudio.google.com/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google AI Studio Pricing&lt;/a&gt;, &lt;a href=&#34;https://www.anthropic.com/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Anthropic Claude Pricing&lt;/a&gt;, and &lt;a href=&#34;https://openai.com/api/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;\n&lt;h2 id=&#34;deepseek-api-for-the-right-reason&#34;&gt;Deepseek api for the right reason.&lt;/h2&gt;\n&lt;p&gt;All the frontier AI models at this point are capable of doing the job they are told to do. There is a saying that you just have to do 80%/90% of the work, and the remaining work I can do myself. A lot of people are feeling that way regarding the AI models. What about them? The AI models have been performing well over the last 6 months; we don&amp;rsquo;t want the last itty-bitty performance for the day-to-day tasks that we do. It is needed for hard problems, sure, but for daily things it&amp;rsquo;s definitely not needed. So, for the reason of being cheap, affordable, and open weights&amp;hellip; I chose to use DeepSeek.&lt;/p&gt;\n&lt;h2 id=&#34;purpose&#34;&gt;Purpose&lt;/h2&gt;\n&lt;p&gt;I mainly started to use the deepseek api for the coding, they offer the model to be compatible with claude code and vscode (github copilot), they have instructions to integrate with every tool you can think of — see the &lt;a href=&#34;https://api-docs.deepseek.com&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;DeepSeek API Documentation&lt;/a&gt; for details. Specifically, for VS Code you can use extensions like &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=Continue.continue&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Continue&lt;/a&gt; or &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Cline&lt;/a&gt; configured to point to the base URL &lt;code&gt;https://api.deepseek.com&lt;/code&gt;, or install the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=deepseek.deepseek-v4-copilot-chat&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;DeepSeek V4 for Copilot Chat&lt;/a&gt; extension. For Claude Code, you can use the Anthropic-compatible endpoint by exporting environment variables before starting the CLI:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;export ANTHROPIC_BASE_URL=&amp;quot;https://api.deepseek.com/anthropic&amp;quot;\nexport ANTHROPIC_AUTH_TOKEN=&amp;quot;&amp;lt;your_deepseek_api_key&amp;gt;&amp;quot;\nexport ANTHROPIC_MODEL=&amp;quot;deepseek-v4-pro[1m]&amp;quot;\nexport ANTHROPIC_DEFAULT_OPUS_MODEL=&amp;quot;deepseek-v4-pro[1m]&amp;quot;\nexport ANTHROPIC_DEFAULT_SONNET_MODEL=&amp;quot;deepseek-v4-pro[1m]&amp;quot;\nexport ANTHROPIC_DEFAULT_HAIKU_MODEL=&amp;quot;deepseek-v4-flash&amp;quot;\nexport CLAUDE_CODE_SUBAGENT_MODEL=&amp;quot;deepseek-v4-flash&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;No matter how much I use, they didn&amp;rsquo;t utilize the money I topped up on their site. It was just $5, but the usage I was doing was hardly making a dent. Given that I was using the pro model for the tasks and the flash model for menial tasks, seeing that it made no difference on the token budget, I started to use the DeepSeek-V4-Pro model for all tasks. Even then&amp;hellip; it made no dent in the billing. I still hadn&amp;rsquo;t utilized that first $5, but after starting to use agents &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; (which I talk about later in the post), I topped up another $5 in the middle of the month.&lt;/p&gt;\n&lt;p&gt;I was mostly using it for coding and simple tasks like automation to do mundane tasks, but just for the one instance, that&amp;rsquo;s it. Since I was doing coding mostly for experimental stuff and not code that I want to be private, I didn&amp;rsquo;t care about the Chinese company using my prompts or data. It&amp;rsquo;s been an agreed-upon truth (for anyone reading from far beyond~ maybe a different era), (everybody in the world agrees upon this one thing) that all the Chinese companies ever do is steal technology and steal data from people around the world. At the point of writing this, maybe things will have changed in the future. There are other service providers that advertise/say they don&amp;rsquo;t train on the API we use (like OpenCode (I&amp;rsquo;m going to try that next month, I&amp;rsquo;ll tell you why in a bit)); they also host the DeepSeek API inside the USA. Since the model is open-weights&amp;hellip; they can just download and host them on their own hardware&amp;hellip; or buy it from vendors inside the USA and serve it to customers, but internally they could be doing anything we don&amp;rsquo;t know.&lt;/p&gt;\n&lt;h2 id=&#34;evolving&#34;&gt;Evolving&lt;/h2&gt;\n&lt;p&gt;As I&amp;rsquo;ve stated before, since there is no cost involved for most of the time when I&amp;rsquo;m using the APIs, I started to use AI agents &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt;. Before, I just used them whenever necessary with different tools, but never ran Hermes or OpenClaw for a long time. I started experimenting with them on my laptop, but then I made the setup on my server—configuring 3 messaging apps and adding others so they can work independently. Now, both the Hermes agent and OpenClaw are running &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; on my server inside a container without sudo access. At first, it was just intrigue: why did others get obsessed with them? But since the cost is low, why not? They work nicely and actually save time by doing work independently.&lt;/p&gt;\n&lt;p&gt;Initially, I liked OpenClaw, but then when I used it more and more, I started to use Hermes more. It is important to note that OpenClaw is written in Node and uses Node for everything when doing tasks, but Hermes is written in Python, so it uses Python everywhere when it&amp;rsquo;s doing tasks.&lt;/p&gt;\n&lt;p&gt;Since there is no cost, I started to use AI agents &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; (Hermes and OpenClaw). Before, I just used them whenever required, so I started experimenting. Instead of just on my laptop, I hosted them on the server, configured 3 messaging apps, and added others. They work nicely and actually save time mostly by doing work independently.&lt;/p&gt;\n&lt;h2 id=&#34;how-much-ai-tokens-did-i-spend-this-month-from-deepseek-directly-updated-after-june&#34;&gt;How much AI tokens did I spend this month from DeepSeek directly? (updated after June)&lt;/h2&gt;\n&lt;p&gt;So for the whole month, since I started using it, I&amp;rsquo;ve used 400M tokens and around $10. I&amp;rsquo;ve already shared how I&amp;rsquo;m running Hermes and OpenClaw &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; above, and I&amp;rsquo;ll cover more details about their implementation in my next post.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2026/deepseek-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s 3:26 AM on a night. I&amp;rsquo;m pondering over things, thinking through things. Things are muddy during the day; I&amp;rsquo;m thinking how can I get this calmness during the day&amp;hellip; silence? The comfort of knowing others are asleep, only I am awake, and no one&amp;hellip; willingly at this hour contacts anyone. I&amp;rsquo;ve gotta find that cheat. Is it the ambiance? Or the feeling of knowing there is still 7 hours of time left&amp;hellip; until I won&amp;rsquo;t be bothered by other things.&lt;/p&gt;\n&lt;p&gt;This month, I&amp;rsquo;m doing something that I wanted to do for a long time: moving out of Chennai and back to my hometown with all my things and stuff. I&amp;rsquo;ve been thinking&amp;hellip; &amp;ldquo;what if&amp;rdquo; this and that for a long time&amp;hellip; but the thing is&amp;hellip; it&amp;rsquo;s good to be prepared. I&amp;rsquo;ve realized I can just do it again when it&amp;rsquo;s required; it&amp;rsquo;s not a big deal. Sure, it can be based on luck, but I&amp;rsquo;m sure I have talented people and smart SAs to sort it out. It&amp;rsquo;s been a long time&amp;hellip; I have an attachment to this place now—my messy table, AC at 21 degrees C on a July night, lights on, curtains half open, fan on top of my head at 55% with rainy weather outside in Chennai and a cold night. Remember this: no glasses, closet behind, half asleep but yet wide awake, thoughts of wanting to sleep so I won&amp;rsquo;t be groggy for work next morning, reflecting, searing memory, what-ifs.\nSorry, all over the place. Swear I&amp;rsquo;m not drunk.&lt;/p&gt;\n&lt;p&gt;Still double-minded. Maybe I should keep a small place for myself alone? On the fence on this. All the actions I take based on this&amp;hellip; are fruitless. I have the weekend to disassemble things; I think it would be a different set of tasks than I usually do. Gosh, to think I was doing a lot of different things 2 and 4 years ago on the weekends than what I am doing currently.&lt;/p&gt;\n&lt;p&gt;What happened to EV S, H, M? What happened to those rules of L? Can&amp;rsquo;t control how and what you are already perceived as. Best to get rid of it, stepping stone, learn?&lt;/p&gt;\n&lt;p&gt;From me talking earlier about GPUs, I&amp;rsquo;m thinking of building a machine just for running desktop-level LLMs. For most not-so-significant knowledge work, we don&amp;rsquo;t need a state-of-the-art model with advanced reasoning. The things people require are context-aware agents/AI that follow the instructions and act accordingly&amp;hellip; that&amp;rsquo;s all there is to it, at least at the current state of LLMs now. All the open-weights models have gotten a lot better over the months; we can host the flash version of those models easily and we are good. But I think I would need a desktop-grade/level GPU&amp;hellip; multiple GPUs, to have that sort of resource available to me. In the future, there are probably cheaper ways to run LLMs, or even better, LLMs won&amp;rsquo;t require GPUs. There are already TPUs from Google and NPUs&amp;hellip; but still the same as a GPU in the future&amp;hellip; or better yet, there are no LLMs at all? AI is completely different.&lt;/p&gt;\n&lt;p&gt;After so long, I am planning to leave my Chennai apartment. I remember writing about it 2 years ago on a December, now on a June. I already informed them that I would be leaving, and I plan to vacate by the end of this month. Will update on this next month. Long story short, they asked me to buy it, but I said no.&lt;/p&gt;\n&lt;!-- maybe I&#39;m just angry at myself for the things I&#39;ve done in my life, it&#39;s not that great? To send in Discord, I stopped saying things but that takes a lot of time, so conserving. When did this change? --&gt;\n&lt;!-- only thing that I have is my studies and my self-work, never give that up for anything, remember the dread, 1:04 AM, can&#39;t keep up doing the same thing --&gt;</description></item><item><title>Comparing Free Chinese Models For Daily Use - May 2026</title><link>//bluepie.in/2026/05/may-2026/</link><pubDate>Fri, 01 May 2026 17:54:17 +0000</pubDate><guid>//bluepie.in/2026/05/may-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I wanted to test the performance and accuracy of the ai models at the time of writing this (May 2026, so I can leverage them in my day-to-day tasks).\nCurrently, I am testing 3 models: 1. Deepseek (v4 just got released) 2. GLM 3. Qwen.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-a-common-task&#34;&gt;Identifying a common task&lt;/h2&gt;\n&lt;p&gt;At the time of writing this, there are Tamil Nadu elections. I&amp;rsquo;ve made up my mind to ask the AI to visualize the TN elections and would rate them based on their accuracy and how beautifully they present the data.&lt;/p&gt;\n&lt;p&gt;Price-wise, Deepseek and the Chinese models are dominating the leaderboards for the price-to-perfomance ratio.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/may-2026/leaderboard-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/may-2026/leaderboard-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;deepseek&#34;&gt;Deepseek&lt;/h2&gt;\n&lt;p&gt;Though the design is okey, the accuracy is great.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/deepseek.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;glm&#34;&gt;GLM&lt;/h2&gt;\n&lt;p&gt;The design is great, especially where each seat is represented as an arc and color-coded, but the data is completely wrong.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/glm.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;qwen&#34;&gt;Qwen&lt;/h2&gt;\n&lt;p&gt;The design is mid, and the accuracy is also wrong.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/qwen.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion-winner&#34;&gt;Conclusion / Winner&lt;/h2&gt;\n&lt;p&gt;From the different conclusions, we have different results. Only the first model&amp;hellip; both the design and the model accuracy are good. Note that we have selected the expert mode.&lt;/p&gt;\n&lt;p&gt;In GLM&amp;hellip; the design is great without even the correct prompt&amp;hellip; but the accuracy is bad.&lt;/p&gt;\n&lt;p&gt;With this short test, I was able to figure out which AI I can rely on the most.&lt;/p&gt;\n&lt;p&gt;Results of AI:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/results-of-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Updated as of May 24th. Got to stay home more even though I had a prod release from the office (working at VZ at this point in time), but food poisoning and dehydration happened to my dad&amp;hellip; had to take him to hospital emergency care, but he recovered quickly. The marriage that is planned in May is in limbo; the partner wants some things to be finished from her side, and I&amp;rsquo;m feeling kinda the same on some matters. My SO is trying to find a good company to work for, in between managing family and me, hard for her as well. Take small steps forward; though it doesn&amp;rsquo;t make any sense at the current moment, in the long run, it will make sense.&lt;/p&gt;\n&lt;p&gt;Since COVID, I took medical insurance for my parents, both of them&amp;hellip; I didn&amp;rsquo;t think it would be of much use when my dad got admitted to the hospital&amp;hellip; but the hospital successfully processed it. In the end, I got the money I paid for the entire hospital visit, though others said they were useless (others meaning some of my brothers and others)&amp;hellip; I think it&amp;rsquo;s useful and provides some mental relief when something does happen in case of emergencies. I have insurance with the office that covers me but also my parents, but I&amp;rsquo;ve kept this separately. Thinking of taking separate health insurance and life insurance; don&amp;rsquo;t know what I&amp;rsquo;ve decided yet. I&amp;rsquo;ll let you know in the future what I decide.&lt;/p&gt;\n&lt;p&gt;Unrelated to this, GeForce Now got announced in India, and I got an invite for the trial. I stopped my gaming and those related activities recently, so I shared it with a friend of mine. He paired it with Xbox Game Pass and is having a good time with different games.&lt;/p&gt;\n&lt;p&gt;Got to meet some old friends that I didn&amp;rsquo;t meet in a long while; I felt human/grounded seeing the changes they have been through. I said this to one even: people are shaped by whom they interact with and whom they surround themselves with; they are sculpted subconsciously without them even realizing it&amp;hellip; seeing how differently they&amp;rsquo;ve changed.&lt;/p&gt;\n&lt;p&gt;The price of petrol has risen up so much&amp;hellip; right after the elections&amp;hellip; the politicians played their hand smartly&amp;hellip; in April 2026&amp;hellip; the price was around 99.82 rupees, and now it has been 106.xx rupees. Because of the ongoing conflict in the Middle East—specifically tensions surrounding the Strait of Hormuz disrupting global oil supply chains, the prices are expected to go even higher? I was planning to buy a good electric scooty, but then my friend&amp;rsquo;s dad said things to convince me not to take it (it was just before the war)&amp;hellip; now I&amp;rsquo;m feeling maybe I shouldn&amp;rsquo;t have brought it up with him, and as a result, wouldn&amp;rsquo;t have been influenced by him. I have to search for one electric scooty that&amp;rsquo;s good for the short term and also for the long term, considering the serviceability and all.&lt;/p&gt;\n&lt;p&gt;On top of that, I have to change the apartment in Chennai. I remember writing in January 2 years ago when I moved into this apartment. I&amp;rsquo;m thinking of moving back to my hometown and staying over at a friend&amp;rsquo;s place shortly&amp;hellip; until I&amp;rsquo;m done with a few things? Idk&amp;hellip; or move to a new apartment in Chennai so I can still stay there and commute to the office&amp;hellip; Idk, I&amp;rsquo;m conflicted. Maybe I should stay in a hotel for the days I&amp;rsquo;m going to the office. Argh! These are just simple decisions but take away valuable time and energy.&lt;/p&gt;\n&lt;p&gt;And lastly, it&amp;rsquo;s been one year, huh! If you know what I mean, I have to update this section later&amp;hellip; about what this is&amp;hellip; at a later point in time.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve started a long stagnated item; I made progress one way or another. Future me, how did it go through? Were you able to handle it? If you wanted to talk to someone about anything, I&amp;rsquo;m here with you; look back&amp;hellip; read these items and reminisce (atow - raining outside, thunderstorm, parents sleeping, open window, sofa near window, wind on the back of the head, midnight).&lt;/p&gt;</description></item><item><title>Offline AI models on Phones &amp; PC - April 2026</title><link>//bluepie.in/2026/04/april-2026/</link><pubDate>Sun, 12 Apr 2026 05:49:01 +0000</pubDate><guid>//bluepie.in/2026/04/april-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Today I want to talk about, &lt;a href=&#34;https://ai.google.dev/gemma/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Gemma 4&lt;/a&gt;; this sets a new standard for open source AI models. I am astonished by the quality of this, though it has a small model size compared to the other big ones out there.. like &lt;a href=&#34;https://kimi.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Kimi K2.5 from Moonshot AI&lt;/a&gt;, that thing is a 1 trillion parameter model.. 1 trillion! and Gemma 4&amp;rsquo;s biggest is only 31 billion.. but here is the thing, Kimi K2.5 only uses 32 billion of those parameters at any given time, so they are not that far apart in practice.. and Gemma 4 punches way above its weight for how small it actually is..&lt;/p&gt;\n&lt;h2 id=&#34;gemma4&#34;&gt;gemma4&lt;/h2&gt;\n&lt;p&gt;With the Gemma models, the naming conventions highlight their architecture, specifically using &lt;code&gt;A&lt;/code&gt; for &amp;ldquo;active&amp;rdquo; parameters and &lt;code&gt;E&lt;/code&gt; for &amp;ldquo;edge&amp;rdquo; or &amp;ldquo;efficient&amp;rdquo; models. For instance, &lt;code&gt;E2B&lt;/code&gt; and &lt;code&gt;E4B&lt;/code&gt; designate lighter models optimized for edge devices. They also utilize a Mixture of Experts (MoE) architecture, like the 26B MoE. In an MoE model, only a subset of the total parameters are &amp;ldquo;active&amp;rdquo; (&lt;code&gt;A&lt;/code&gt;) for any given token. Think of it like a restaurant with 26 chefs, but only 4 are actually cooking your order at any point while the rest stand by with their own specializations. This allows it to run as fast as a 4B model while retaining the extensive knowledge of a 26B one. On the other hand, there is the 31B Dense model. That one just loads everything—all 31 billion parameters fire for every single token. No routing, no tricks, just brute force. It uses much more memory, but its performance is more predictable.&lt;/p&gt;\n&lt;p&gt;There are two classifications of models, and in each of these classifications there are multiple models that are released. Two of the four.. are for desktop/laptop, and the others, they created for edge devices. What they mean by edge devices are mobile phones, Raspberry Pi, and other less powerful devices or mobile.&lt;/p&gt;\n&lt;h2 id=&#34;on-pc&#34;&gt;On PC&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been a long time user of &lt;a href=&#34;https://lmstudio.ai/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;lm studio&lt;/a&gt;, it offers great features.. I like it because it offers ease of use, ability to manage different models all at once,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/lm-studio-ui.webp&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;from the official documentation. I can run up to 14 GB of graphics memory worth of model. The 31B dense model needs around 20+ GB at full precision.. so that is out for me. The 26B MoE is interesting though.. even though it has 26B total params you only compute 4B at a time, so it is lighter on the processor, but you still have to load all 26B into VRAM which I don&amp;rsquo;t have.. at least until now. The trick people use is Q4 quantization, which basically compresses the model down significantly. The 31B at Q4 can fit around 16-17 GB.. still a bit above what I got but getting closer. Maybe next GPU.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-laptop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;on-mobile&#34;&gt;On mobile&lt;/h2&gt;\n&lt;p&gt;It runs surprisingly fast and remains fairly accurate. The output isn&amp;rsquo;t exactly the same as the desktop models, largely because of how mobile inference is optimized. Instead of performing full, heavy computations on every layer from scratch for every word, mobile execution uses techniques like quantization and KV caching. Essentially, the model reuses pre-calculated states (like a memory map) on each layer for the context it has already processed, which drastically speeds up generation on phones. Because of these aggressive optimizations, it&amp;rsquo;s hard to measure its accuracy strictly against the uncompressed desktop versions. Still, to give it credit, it handles an impressive 140 languages easily; heck, I even used my native language and it worked great when I tried it.&lt;/p&gt;\n&lt;p&gt;To run these models locally, I am using &lt;strong&gt;&lt;a href=&#34;https://github.com/google-ai-edge/gallery&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google AI Edge Gallery&lt;/a&gt;&lt;/strong&gt;. It is an open-source, experimental mobile application from Google that showcases running Large Language Models entirely on-device (Android and iOS). Because the app uses &lt;a href=&#34;https://ai.google.dev/edge/litert&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiteRT&lt;/a&gt; (formerly TensorFlow Lite) and the &lt;a href=&#34;https://ai.google.dev/edge/mediapipe/solutions/genai/llm_inference&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LLM Inference API&lt;/a&gt;, the models execute offline directly on the phone&amp;rsquo;s hardware. This means complete data privacy with no cloud API costs. The app provides great features out of the box like LLM Chat, multimodal interactions to ask questions about images, a Prompt Lab for testing ideas locally.. and much more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-mobile.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal&#34;&gt;personal&lt;/h2&gt;\n&lt;p&gt;Hey, how are you doing? I hope everything is going well. I lost my iPhone this month; it was hectic. I lost it when traveling back from the office to my Chennai home. Although I have had the phone for a while now, and it&amp;rsquo;s old.. it still stings. My first time losing a phone. Why would I lose an iPhone? It was a series of events that happened that day. A few days before.. I accidentally bent the new glasses I got. Then during that day, the glasses got so loose that I couldn&amp;rsquo;t wear them the entire day. The temples (new term.. weird to call that piece that.. anyway) were so loose, they would have come into two pieces if I had worn them that day. So before that, a day ago, my mom got into a little accident. All of these in mind, I got into a weird headspace and it led to me losing the phone. I felt something drop.. but when I looked for it, it was still there where I kept the phone. On top of it, the pouch that I kept the phone in was torn a bit and I hadn&amp;rsquo;t changed it.. which also contributed to this. I&amp;rsquo;m never keeping a phone in a pouch again! ..that&amp;rsquo;s what I told myself, but future me will catch myself doing the same in the future, I think. After that.. I lost my building entrance card. My office card I had, but I lost the Olympia Guindy building card, which is another pain.. but at the time of updating this, on April 15, it was found and I went and collected it.&lt;/p&gt;\n&lt;p&gt;It&amp;rsquo;s been a long time since I bought a phone. Before, I used to develop for Android and iOS; now I don&amp;rsquo;t anymore. I don&amp;rsquo;t think I need an iPhone again.. a better deal is to buy a latest &lt;a href=&#34;https://www.qualcomm.com/snapdragon&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Snapdragon chipset&lt;/a&gt; phone and call it a day. Going to switch back to bigger XL screen phones.&lt;/p&gt;\n&lt;p&gt;Lots of things to do, yet you choose to have little moments of fun? At the moment, my brain can&amp;rsquo;t remember what pending things are left.&lt;/p&gt;\n&lt;p&gt;A friend stayed over for a while at the start of April. I had to work on weekends because of horrible management in the office. I try to do a small piece of work every day though, but still.. yeah. He was going on a trip.. north and back and stayed for a while. The initial plan was that he would stay here for a while, but then we would both go to our hometown. He is a childhood friend of mine, but work got hectic; I couldn&amp;rsquo;t make it as discussed next week and he had to go alone.&lt;/p&gt;\n&lt;p&gt;Last year at this time, I was playing &lt;a href=&#34;https://www.playstation.com/games/ghost-of-tsushima/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Ghost of Tsushima&lt;/a&gt; in my off-time. This year, I&amp;rsquo;m playing &lt;a href=&#34;https://megacrit.com/slaythespire2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Slay the Spire 2&lt;/a&gt; and &lt;a href=&#34;https://crimsondesert.pearlabyss.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Crimson Desert&lt;/a&gt; (released on March 19th, 2026) in my off-time.. dang nab it, I should give up this gaming.. I&amp;rsquo;m getting old.. but what if that changes me entirely? Risky..&lt;/p&gt;</description></item><item><title>&#39;AI Skills&#39;</title><link>//bluepie.in/2026/03/mar-2026/</link><pubDate>Tue, 03 Mar 2026 05:24:25 +0000</pubDate><guid>//bluepie.in/2026/03/mar-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Anthropic came and published their skills documentation on what they are roughly 2 months ago, but I suspect they and other AI companies were using them internally even before they published. The idea of people seeing them as a new thing and being astonished is abysmal..because we were already using it..but without the naming convention and the optimised context limiting. The concept is simple: you create a folder named skill you want, then inside it, the main file is called skill.md. This takes less context when ai reads it, as it just reads the frontmatter alone; you write detailed instructions below the frontmatter with different scripts and whatever that is necessary to finish that task/skill, you also writter a condition upon which the ai will trigger. The AI then keeps all these details and uses it ad hoc. The problem it solves is that this uses way less context than mcp or others and only triggers when necessary.&lt;/p&gt;\n&lt;h2 id=&#34;example-plantuml-generator&#34;&gt;Example: PlantUML Generator&lt;/h2&gt;\n&lt;p&gt;So the idea is simple; as you can infer from above, let&amp;rsquo;s go with an example. I wanted to create an architecture diagram skill in office. ..in office we use he PlantUML to create sequence diagrams.&lt;/p&gt;\n&lt;p&gt;The problem is you have to ask the AI to generate it, and then generate the image and maintain the image somewhere. The problem with this is it takes time and its a manual process. How can we use skills to automate this? We can write instructions to ask the AI chat / agent to be like, if a user asks x, then do the process and update the diagram. We can add these instructions in the skill.md file, keep the name and descriptions to work with the architecture diagram.&lt;/p&gt;\n&lt;p&gt;You can write scripts and then use them to generate things; the AI will execute them based on the instructions. The next is dependencies, if any. Since PlantUML is an open-source project, so we can download the jar file and put that in the skills folder. since its a java project..it&amp;rsquo;s platform independent, so no need to worry about different binaries&lt;/p&gt;\n&lt;p&gt;So everything can be packaged into a single skill.&lt;/p&gt;\n&lt;p&gt;you can see the skills folder here: &lt;a href=&#34;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;embedding-code-in-images&#34;&gt;Embedding Code in Images&lt;/h2&gt;\n&lt;p&gt;On top of the skill, I was thinking about for a optimization. In college, I conducted a Capture the Flag; the event was like Google CTF and other CTF events from that time. I was inspired by them, Anyway. one of the competitions was to find the hidden details from the image. Inside an image, you can add a data to it, it can be any data. So I was thinking, why can&amp;rsquo;t we use the same thing here? So I asked ai to write a script to embed code into the images. So the PlantUML diagram is stored inside the images, the images are the output of the code. It&amp;rsquo;s like a chicken and egg problem once you delete the code file from the repo.&lt;/p&gt;\n&lt;p&gt;We want crud options on this; the update image, edit image, and create image are just a set of instructions that the AI can read and execute.. in my case it will be Python scripts, when user asks to edit, it will extract the code from the image and create a temporary file we can work on, and when we ask to store it, it saves the source code, again inside the image, deleting the code file.&lt;/p&gt;\n&lt;h2 id=&#34;other-skills&#34;&gt;Other Skills&lt;/h2&gt;\n&lt;p&gt;There are other skills that I use here, it keeps changing overtime.&lt;/p&gt;\n&lt;p&gt;The main one I wanted to share is the PlantUML generator; it has all the features that I need to be adding to demonstrate the features of the skill.&lt;/p&gt;\n&lt;h2 id=&#34;sub-agents-lifecycle-hooks&#34;&gt;Sub-agents &amp;amp; Lifecycle Hooks&lt;/h2&gt;\n&lt;p&gt;At the end of Feb 2026, the Claude team updated the skillset to have custom features, like sub-agents. What it is, is that the sub-agent is being called by the main agent when doing a certain task.. so it will fix the context problem. it&amp;rsquo;s one of the major updates to skills; it&amp;rsquo;s the major one that makes a significant difference, because it won&amp;rsquo;t clutter the main context; the main agent is not interfered with.&lt;/p&gt;\n&lt;p&gt;It also has access to the lifecycle events, Lifecycle hooks (on_start, on_success, on_error, PreToolUse, etc.).. but i haven&amp;rsquo;t adopted them yet; but this will help me with the cryo vault project: &lt;a href=&#34;https://github.com/aghontpi/cryo-vault&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/cryo-vault&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m having to juggle a lot of things in my life currently; finding focus time to think on what is essential or next is hard with this attention-seeking world; socializing; maintaining health and ad hoc new plans; argh! I started to have a new habit; hopefully, when I am consistent on this.. I&amp;rsquo;ll post on this later!.. future me..! what is happening? hope you are good!&lt;/p&gt;</description></item><item><title>Selfhosting an Automation tool - N8N</title><link>//bluepie.in/2026/02/feb-2026-2/</link><pubDate>Sat, 28 Feb 2026 15:17:41 +0000</pubDate><guid>//bluepie.in/2026/02/feb-2026-2/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;In the previous post, I was talking about &lt;a href=&#34;https://claude.com/skills&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;claude&lt;/a&gt; skills. This is about automation, no code/ minimal solutions. At this point, it&amp;rsquo;s been over 1.5 to 2 years since &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and automation; nowadays it&amp;rsquo;s easy to just build an app/ ask AI to build the app instead of creating the automation, it&amp;rsquo;s easier that way, but regardless, let&amp;rsquo;s see how the future goes; I&amp;rsquo;m already seeing the trend of creating custom apps for each one&amp;rsquo;s personal preference.&lt;/p&gt;\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;Instead of creating projects for each use and hosting it for every use case, I thought of using automation tools and solving those problems; for ex, one such problem is creating a diet maintainer; I ranted in a previous post that I gained a couple of kgs in a short while, but I&amp;rsquo;m trying to reduce it; so I have to keep track of the amount of calories, etc., but I want this to be an Excel sheet for tracking, yes there are apps for that; but I wanted a simple chat interface where I can type the food I ate and when I ate it, and it should approximate the calories, and when I ask how much is left, it should give back the amount of food that I can eat in the remaining part of the day/ from that point onwards.&lt;/p&gt;\n&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;\n&lt;p&gt;I choose n8n. The main reason I prefer it is the immense community support it has, it&amp;rsquo;s incredibly popular, which means if I run into a wall, someone else has probably already solved it and posted about it. Plus, being able to tinker with it under the hood while self-hosting is a massive bonus compared to locked-in cloud alternatives.&lt;/p&gt;\n&lt;p&gt;Speaking of alternatives, there are quite a few out there:\n- &lt;a href=&#34;https://www.make.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Make (formerly Integromat)&lt;/a&gt;: Great visual builder,\n- &lt;a href=&#34;https://zapier.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Zapier&lt;/a&gt;: The biggest name out there with the most integrations,\n- &lt;a href=&#34;https://pipedream.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Pipedream&lt;/a&gt;: Fantastic for developers who want to write code snippets, but maybe overkill if you just want pure drag-and-drop.\n- &lt;a href=&#34;https://www.activepieces.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Activepieces&lt;/a&gt;: is similar to n8n&lt;/p&gt;\n&lt;h2 id=&#34;self-hosting&#34;&gt;Self hosting&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Here is the official n8n documentation guide&lt;/a&gt;, I started to see the official docs of n8n. One thing I got surprised by is that they have incorporated AI, and that AI is surprisingly good at providing the right instructions, because when you host with different servers, server config differs for each one like &lt;a href=&#34;https://caddyserver.com/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;caddy&lt;/a&gt; / &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;/ etc., and for automation to work, it has to be able to receive webhooks, and alongside things like if it&amp;rsquo;s behind a proxy; it must be configured to tell that you are behind a proxy, and based on that it can handle the incoming request, etc.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll save you the trouble and give the config here; basically I was able to set up the Nginx server pretty easily using the AI bot that they have incorporated in their site. Below is all the config I needed to be able to run this from &lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; and &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;:&lt;/p&gt;\n&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I like to run things with &lt;a href=&#34;https://docs.podman.io/en/latest/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;podman&lt;/a&gt;/&lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; as it removes the dependency from my cloud machine.&lt;/p&gt;\n&lt;p&gt;Obviously I have to configure my domain name to &lt;a href=&#34;https://www.godaddy.com/help&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;godaddy&lt;/a&gt;/&lt;a href=&#34;https://developers.cloudflare.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt; and deploy SSL certificates; I&amp;rsquo;ve covered that plenty in my other posts:&lt;/p&gt;\n&lt;p&gt;docker config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;podman run -d --restart unless-stopped \\\n--name n8n \\\n-p 5678:5678 \\\n-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \\\n-e N8N_RUNNERS_ENABLED=true \\\n-e WEBHOOK_URL=https://&amp;lt;server-name&amp;gt;/ \\\n-e N8N_PROXY_HOPS=1 \\\n-e N8N_HOST=&amp;lt;server-name&amp;gt;\n-e N8N_PORT=5678 \\\n-e N8N_PROTOCOL=https \\\n-v /mnt/podman-data/n8n_data:/home/node/.n8n:Z \\\ndocker.n8n.io/n8nio/n8n\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;nginx config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;server {\nlisten 80;\nserver_name &amp;lt;server-name&amp;gt;\nlocation / {\nproxy_pass http://127.0.0.1:5678;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_set_header X-Forwarded-Port $server_port;\nchunked_transfer_encoding off;\nproxy_buffering off;\nproxy_cache off;\n}\nlocation /webhook/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\nlocation /webhook-test/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-how-to-configure&#34;&gt;Configuring - How to configure&lt;/h2&gt;\n&lt;p&gt;Modifying a telegram bot to track the calory intake of the day based on estimates from me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/overview.png&#34; alt=&#34;Overview&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is initial learning time to get used to what each option is, but when you get the hang of it, it&amp;rsquo;s pretty easy, since I&amp;rsquo;ve done drag and drop editors before; I caught up easily in approx 5-10ish mins.&lt;/p&gt;\n&lt;p&gt;Create a &lt;a href=&#34;https://core.telegram.org/bots/api&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;telegram&lt;/a&gt; node and select the appropriate Telegram account credential. Set the &lt;strong&gt;Trigger On&lt;/strong&gt; parameter to &lt;code&gt;Message&lt;/code&gt; so it triggers on new incoming messages of any kind (text, photo, sticker, etc.). Note that due to Telegram API limitations, you can only use one Telegram trigger per bot. Also ensure there is validation to only process messages from your known user ID, otherwise the bot will refuse to respond.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/telegram-trigger-receive.png&#34; alt=&#34;Telegram Trigger Settings&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Add an &lt;strong&gt;Analyze Text Message&lt;/strong&gt; node and connect it to your Google Gemini (or equivalent API) configuration. Set the Resource to &lt;code&gt;Text&lt;/code&gt; and Operation to &lt;code&gt;Message a Model&lt;/code&gt;, using the &lt;code&gt;models/gemini-2.5-flash&lt;/code&gt; model so its fast and reliable. models cheaper than 2.5-flash are not reliable based on testing. Within the node, establish a system prompt (Role: &lt;code&gt;Model&lt;/code&gt;) instructing the AI to process user input into structured nutritional data—identifying meal components, estimating portion weights, and warning it not to reveal its internal steps. Pass the user input as a prompt (Role: &lt;code&gt;User&lt;/code&gt;) using the expression &lt;code&gt;{{ $json.message }}&lt;/code&gt; along with today&amp;rsquo;s date context: &lt;code&gt;{{ $today.format(&#39;yyyy-MM-dd&#39;) }}&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/using-gemini-ai-via-api.png&#34; alt=&#34;AI Processing Configuration&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;To store the parsed responses, add a Google Sheets node configured to &amp;ldquo;Append&amp;rdquo; rows, and set it to &lt;strong&gt;Map Each Column Manually&lt;/strong&gt;. You can map fields originating from the trigger and model response:\n- &lt;strong&gt;User_ID&lt;/strong&gt;: &lt;code&gt;{{ $json.chat_id }}&lt;/code&gt;\n- &lt;strong&gt;Date&lt;/strong&gt;: Extract and format the date directly with &lt;code&gt;{{ DateTime.fromSeconds($json.date).format(&#39;yyyy-LL-d&#39;) }}&lt;/code&gt;.\n- Dietary metrics like &lt;strong&gt;Meal_description&lt;/strong&gt;, &lt;strong&gt;Calories&lt;/strong&gt;, &lt;strong&gt;Proteins&lt;/strong&gt;, &lt;strong&gt;Carbs&lt;/strong&gt;, and &lt;strong&gt;Fats&lt;/strong&gt; can be defined automatically by mapping them from structured outputs generated by the AI model.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/append-to-excel-sheet.png&#34; alt=&#34;Append to Google Sheets&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is all sorts of existing nodes (in &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;, a &amp;ldquo;node&amp;rdquo; is basically a building block or a step in your automation workflow—it can be a trigger that starts the flow, an action that connects to a specific app&amp;rsquo;s API like sending a message, or a logic step that filters or processes data) that we can use. For this use case, reusing the &lt;a href=&#34;https://developers.google.com/sheets/api/guides/concepts&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google sheet&lt;/a&gt; node from official requires getting API keys, but that is well documented by &lt;a href=&#34;https://docs.n8n.io/integrations/builtin/credentials/google/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and &lt;a href=&#34;https://developers.google.com/workspace/guides/create-credentials&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;Finally, configure another Telegram node to send a reply back to the user. Set the Resource to &lt;code&gt;Message&lt;/code&gt; and Operation to &lt;code&gt;Send Message&lt;/code&gt;. For the &lt;strong&gt;Chat ID&lt;/strong&gt;, retrieve it from the initial trigger using &lt;code&gt;{{ $(&#39;Telegram Trigger&#39;).item.json.message.chat.id }}&lt;/code&gt;. Include the formatted Text from the AI calculation &lt;code&gt;{{ $json.message }}&lt;/code&gt;. Under Additional Fields, be sure to set &lt;strong&gt;Parse Mode&lt;/strong&gt; to &lt;code&gt;MarkdownV2&lt;/code&gt; so any formatting from the AI looks correct, and toggle off &amp;ldquo;Append n8n Attribution&amp;rdquo; for a cleaner message.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/send-telegram-msg-back.png&#34; alt=&#34;Send Telegram Summary Message&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For the initial setup, because it&amp;rsquo;s new, I was experimenting and took 4-5 hours, because it was interesting; but been using that for maintaining calories now; it&amp;rsquo;s great.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;AI is more prevalent these days, instead of building these automation no code solutions via these tools, users directly build with AI, there are pros and cons.. it&amp;rsquo;s already deployed on the server (the automation tool, one time setup), can deploy any number of times.. limit depends on the server limits, I&amp;rsquo;m okay.. but people need to know of how to debug in case of error, I mean there is AI for that too; if using &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;/automation.. everything is contained within this little confinement work area.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll be trying out other automation experiments, this is serving me well, recently I gained a lot of weight, though not overweight.. I still look fit from an outside view; but a little fat in stomach and back.. I started eating snacks every night and lots of oats (used to prepare the previous night with milk and sugar.. keep it in freezer).. the next morning eating it, I drove this diet for some time, it was good for my morning routine (not going to office on an empty stomach), but the problem was I gained like 5 kgs, I have never been this much weight in my entire life! but I&amp;rsquo;m 6ft.. so the weight is kinda explainable.&lt;/p&gt;\n&lt;p&gt;Me when 76kgs, ideal for my height, less fat content ratio in my body; this is before I got muscle buildup.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/me-when-76.jpg&#34; alt=&#34;image when I was 76kgs&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;but in terms of physique, , I got visibly more muscle development..after above image, idk.. started to go to gym again, let&amp;rsquo;s see, till now I&amp;rsquo;ve been continuing to do it every day except Sunday at home. But I&amp;rsquo;ll track the progress.&lt;/p&gt;\n&lt;p&gt;Updating this part as of March 2026, it&amp;rsquo;s happening again, I feel out of place, everyone around here is behind.. so behind .. than I expect them to be in, when I see, I see a fine line between things and it&amp;rsquo;s like I can slip in between them.. and go past beyond. I see it like this, a highway road that is busy with vehicles going left and right, but I see a path in slow motion that I can slip in between and move to the other side, I see what others are not seeing, IDK how long I will be blessed with this gift or curse that I have, can&amp;rsquo;t share this with anyone, I exposed this lil bit to sq#3, ~Future me is this happening again? Hope you landed on things, learned from past.&lt;/p&gt;</description></item><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item></channel></rss>"}, "f9d7b833e5dec44b6420d0a63d5352f5200ae1a6": {"t": 1785770720.9776938, "n": 161063, "h": "85a85022be21a2b4", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Thu, 18 Jun 2026 21:50:43 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>June 2026 - Deepseek API For Performance &amp; Cost</title><link>//bluepie.in/2026/06/june-2026/</link><pubDate>Thu, 18 Jun 2026 21:50:43 +0000</pubDate><guid>//bluepie.in/2026/06/june-2026/</guid><description>\n&lt;blockquote&gt;\n&lt;p&gt;(following post has been updated 5th of july, 2026)&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;intro&#34;&gt;Intro&lt;/h3&gt;\n&lt;p&gt;A shift in LLM/AI usage: GitHub Copilot changed its user pricing. Microsoft changed the pricing. Nvidia &amp;amp; AMD are coming up with new laptops with 128 or more GB of VRAM to run agents and LLMs locally! But the prices of the chips are not going down; at the time of writing, the Nvidia RTX 5070 16GB is 65k INR.&lt;/p&gt;\n&lt;p&gt;The company that I work for at the time of writing this broke the Microsoft GitHub Copilot tie-up and looked for other vendors. They did it so fast: they sent a company-wide email stating it would be terminated immediately for the FTE (own employees) developers but will be kept only for the vendors, and asked users to utilize and adopt Claude Code. So we started using that, but a lot of people just use it for the sake of it to finish the job. At this point, there is no auto mode in the Claude version they allow inside the organization; people just set the model to high and burn through the tokens. It&amp;rsquo;s been 2 days and people went ham on it, and already 25% of the team budget is gone. We can check out individual budgets and the team&amp;rsquo;s budget at the usage metrics.&lt;/p&gt;\n&lt;p&gt;For me, I got around 300 dollars of AI credits at my disposal: $100 for Claude and around 200 for Cursor. To think that is around 28k INR—the salary I got at the time of starting my career when I left college. Back then, was it considered good money? Things changed really fast and got pricey, huh? These token budgets are arbitrary values set by companies on their own terms and bullshit logic. We are paying money and allocating budget for thin air—tokens? A decade ago, people would laugh at the idea! That would be 2016. I remember 2016; it was a different time: peaceful, chaotic, it had its charms.&lt;/p&gt;\n&lt;h2 id=&#34;deepseek-api-for-the-win&#34;&gt;Deepseek api For The Win&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using the DeepSeek API directly for the entire month of June 2026. It&amp;rsquo;s been great; they offer cheap prices for the latest model they have (at the point of writing this in June/July 2026 (this line was edited on July 5th, 2026, 1:08 AM)). Here is a comparison of the API pricing per 1 million tokens (as of July 2026):&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th align=&#34;left&#34;&gt;Provider&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Model&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Input Price (Cache Miss)&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Input Price (Cache Hit)&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;DeepSeek&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;deepseek-v4-flash&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.14&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.0028&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.28&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;deepseek-v4-pro&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.435&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.003625&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.87&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Google&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Gemini 2.5 Flash-Lite&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.10&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;—&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.40&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Gemini 3.5 Flash&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$1.50&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;—&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$9.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Gemini 3.1 Pro Preview&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$2.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;—&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$12.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Anthropic&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Claude Haiku 4.5&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$1.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.10 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$5.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Claude Sonnet 5&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$3.00*&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.30 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$15.00*&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Claude Opus 4.8&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$5.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.50 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$25.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;OpenAI&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;GPT-5.4 Nano&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.20&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.02 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$1.25&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;GPT-5.4 (Production)&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$2.50&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.25 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$15.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;GPT-5.5 (Flagship)&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$5.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.50 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$30.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;em&gt;*Note: Claude Sonnet 5 features introductory pricing of $2.00/$10.00 per million tokens through August 31, 2026. For verification, check the official docs: &lt;a href=&#34;https://platform.deepseek.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;DeepSeek Pricing&lt;/a&gt;, &lt;a href=&#34;https://aistudio.google.com/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google AI Studio Pricing&lt;/a&gt;, &lt;a href=&#34;https://www.anthropic.com/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Anthropic Claude Pricing&lt;/a&gt;, and &lt;a href=&#34;https://openai.com/api/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;\n&lt;h2 id=&#34;deepseek-api-for-the-right-reason&#34;&gt;Deepseek api for the right reason.&lt;/h2&gt;\n&lt;p&gt;All the frontier AI models at this point are capable of doing the job they are told to do. There is a saying that you just have to do 80%/90% of the work, and the remaining work I can do myself. A lot of people are feeling that way regarding the AI models. What about them? The AI models have been performing well over the last 6 months; we don&amp;rsquo;t want the last itty-bitty performance for the day-to-day tasks that we do. It is needed for hard problems, sure, but for daily things it&amp;rsquo;s definitely not needed. So, for the reason of being cheap, affordable, and open weights&amp;hellip; I chose to use DeepSeek.&lt;/p&gt;\n&lt;h2 id=&#34;purpose&#34;&gt;Purpose&lt;/h2&gt;\n&lt;p&gt;I mainly started to use the deepseek api for the coding, they offer the model to be compatible with claude code and vscode (github copilot), they have instructions to integrate with every tool you can think of — see the &lt;a href=&#34;https://api-docs.deepseek.com&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;DeepSeek API Documentation&lt;/a&gt; for details. Specifically, for VS Code you can use extensions like &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=Continue.continue&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Continue&lt;/a&gt; or &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Cline&lt;/a&gt; configured to point to the base URL &lt;code&gt;https://api.deepseek.com&lt;/code&gt;, or install the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=deepseek.deepseek-v4-copilot-chat&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;DeepSeek V4 for Copilot Chat&lt;/a&gt; extension. For Claude Code, you can use the Anthropic-compatible endpoint by exporting environment variables before starting the CLI:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;export ANTHROPIC_BASE_URL=&amp;quot;https://api.deepseek.com/anthropic&amp;quot;\nexport ANTHROPIC_AUTH_TOKEN=&amp;quot;&amp;lt;your_deepseek_api_key&amp;gt;&amp;quot;\nexport ANTHROPIC_MODEL=&amp;quot;deepseek-v4-pro[1m]&amp;quot;\nexport ANTHROPIC_DEFAULT_OPUS_MODEL=&amp;quot;deepseek-v4-pro[1m]&amp;quot;\nexport ANTHROPIC_DEFAULT_SONNET_MODEL=&amp;quot;deepseek-v4-pro[1m]&amp;quot;\nexport ANTHROPIC_DEFAULT_HAIKU_MODEL=&amp;quot;deepseek-v4-flash&amp;quot;\nexport CLAUDE_CODE_SUBAGENT_MODEL=&amp;quot;deepseek-v4-flash&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;No matter how much I use, they didn&amp;rsquo;t utilize the money I topped up on their site. It was just $5, but the usage I was doing was hardly making a dent. Given that I was using the pro model for the tasks and the flash model for menial tasks, seeing that it made no difference on the token budget, I started to use the DeepSeek-V4-Pro model for all tasks. Even then&amp;hellip; it made no dent in the billing. I still hadn&amp;rsquo;t utilized that first $5, but after starting to use agents &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; (which I talk about later in the post), I topped up another $5 in the middle of the month.&lt;/p&gt;\n&lt;p&gt;I was mostly using it for coding and simple tasks like automation to do mundane tasks, but just for the one instance, that&amp;rsquo;s it. Since I was doing coding mostly for experimental stuff and not code that I want to be private, I didn&amp;rsquo;t care about the Chinese company using my prompts or data. It&amp;rsquo;s been an agreed-upon truth (for anyone reading from far beyond~ maybe a different era), (everybody in the world agrees upon this one thing) that all the Chinese companies ever do is steal technology and steal data from people around the world. At the point of writing this, maybe things will have changed in the future. There are other service providers that advertise/say they don&amp;rsquo;t train on the API we use (like OpenCode (I&amp;rsquo;m going to try that next month, I&amp;rsquo;ll tell you why in a bit)); they also host the DeepSeek API inside the USA. Since the model is open-weights&amp;hellip; they can just download and host them on their own hardware&amp;hellip; or buy it from vendors inside the USA and serve it to customers, but internally they could be doing anything we don&amp;rsquo;t know.&lt;/p&gt;\n&lt;h2 id=&#34;evolving&#34;&gt;Evolving&lt;/h2&gt;\n&lt;p&gt;As I&amp;rsquo;ve stated before, since there is no cost involved for most of the time when I&amp;rsquo;m using the APIs, I started to use AI agents &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt;. Before, I just used them whenever necessary with different tools, but never ran Hermes or OpenClaw for a long time. I started experimenting with them on my laptop, but then I made the setup on my server—configuring 3 messaging apps and adding others so they can work independently. Now, both the Hermes agent and OpenClaw are running &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; on my server inside a container without sudo access. At first, it was just intrigue: why did others get obsessed with them? But since the cost is low, why not? They work nicely and actually save time by doing work independently.&lt;/p&gt;\n&lt;p&gt;Initially, I liked OpenClaw, but then when I used it more and more, I started to use Hermes more. It is important to note that OpenClaw is written in Node and uses Node for everything when doing tasks, but Hermes is written in Python, so it uses Python everywhere when it&amp;rsquo;s doing tasks.&lt;/p&gt;\n&lt;p&gt;Since there is no cost, I started to use AI agents &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; (Hermes and OpenClaw). Before, I just used them whenever required, so I started experimenting. Instead of just on my laptop, I hosted them on the server, configured 3 messaging apps, and added others. They work nicely and actually save time mostly by doing work independently.&lt;/p&gt;\n&lt;h2 id=&#34;how-much-ai-tokens-did-i-spend-this-month-from-deepseek-directly-updated-after-june&#34;&gt;How much AI tokens did I spend this month from DeepSeek directly? (updated after June)&lt;/h2&gt;\n&lt;p&gt;So for the whole month, since I started using it, I&amp;rsquo;ve used 400M tokens and around $10. I&amp;rsquo;ve already shared how I&amp;rsquo;m running Hermes and OpenClaw &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; above, and I&amp;rsquo;ll cover more details about their implementation in my next post.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2026/deepseek-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s 3:26 AM on a night. I&amp;rsquo;m pondering over things, thinking through things. Things are muddy during the day; I&amp;rsquo;m thinking how can I get this calmness during the day&amp;hellip; silence? The comfort of knowing others are asleep, only I am awake, and no one&amp;hellip; willingly at this hour contacts anyone. I&amp;rsquo;ve gotta find that cheat. Is it the ambiance? Or the feeling of knowing there is still 7 hours of time left&amp;hellip; until I won&amp;rsquo;t be bothered by other things.&lt;/p&gt;\n&lt;p&gt;This month, I&amp;rsquo;m doing something that I wanted to do for a long time: moving out of Chennai and back to my hometown with all my things and stuff. I&amp;rsquo;ve been thinking&amp;hellip; &amp;ldquo;what if&amp;rdquo; this and that for a long time&amp;hellip; but the thing is&amp;hellip; it&amp;rsquo;s good to be prepared. I&amp;rsquo;ve realized I can just do it again when it&amp;rsquo;s required; it&amp;rsquo;s not a big deal. Sure, it can be based on luck, but I&amp;rsquo;m sure I have talented people and smart SAs to sort it out. It&amp;rsquo;s been a long time&amp;hellip; I have an attachment to this place now—my messy table, AC at 21 degrees C on a July night, lights on, curtains half open, fan on top of my head at 55% with rainy weather outside in Chennai and a cold night. Remember this: no glasses, closet behind, half asleep but yet wide awake, thoughts of wanting to sleep so I won&amp;rsquo;t be groggy for work next morning, reflecting, searing memory, what-ifs.\nSorry, all over the place. Swear I&amp;rsquo;m not drunk.&lt;/p&gt;\n&lt;p&gt;Still double-minded. Maybe I should keep a small place for myself alone? On the fence on this. All the actions I take based on this&amp;hellip; are fruitless. I have the weekend to disassemble things; I think it would be a different set of tasks than I usually do. Gosh, to think I was doing a lot of different things 2 and 4 years ago on the weekends than what I am doing currently.&lt;/p&gt;\n&lt;p&gt;What happened to EV S, H, M? What happened to those rules of L? Can&amp;rsquo;t control how and what you are already perceived as. Best to get rid of it, stepping stone, learn?&lt;/p&gt;\n&lt;p&gt;From me talking earlier about GPUs, I&amp;rsquo;m thinking of building a machine just for running desktop-level LLMs. For most not-so-significant knowledge work, we don&amp;rsquo;t need a state-of-the-art model with advanced reasoning. The things people require are context-aware agents/AI that follow the instructions and act accordingly&amp;hellip; that&amp;rsquo;s all there is to it, at least at the current state of LLMs now. All the open-weights models have gotten a lot better over the months; we can host the flash version of those models easily and we are good. But I think I would need a desktop-grade/level GPU&amp;hellip; multiple GPUs, to have that sort of resource available to me. In the future, there are probably cheaper ways to run LLMs, or even better, LLMs won&amp;rsquo;t require GPUs. There are already TPUs from Google and NPUs&amp;hellip; but still the same as a GPU in the future&amp;hellip; or better yet, there are no LLMs at all? AI is completely different.&lt;/p&gt;\n&lt;p&gt;After so long, I am planning to leave my Chennai apartment. I remember writing about it 2 years ago on a December, now on a June. I already informed them that I would be leaving, and I plan to vacate by the end of this month. Will update on this next month. Long story short, they asked me to buy it, but I said no.&lt;/p&gt;\n&lt;!-- maybe I&#39;m just angry at myself for the things I&#39;ve done in my life, it&#39;s not that great? To send in Discord, I stopped saying things but that takes a lot of time, so conserving. When did this change? --&gt;\n&lt;!-- only thing that I have is my studies and my self-work, never give that up for anything, remember the dread, 1:04 AM, can&#39;t keep up doing the same thing --&gt;</description></item><item><title>Comparing Free Chinese Models For Daily Use - May 2026</title><link>//bluepie.in/2026/05/may-2026/</link><pubDate>Fri, 01 May 2026 17:54:17 +0000</pubDate><guid>//bluepie.in/2026/05/may-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I wanted to test the performance and accuracy of the ai models at the time of writing this (May 2026, so I can leverage them in my day-to-day tasks).\nCurrently, I am testing 3 models: 1. Deepseek (v4 just got released) 2. GLM 3. Qwen.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-a-common-task&#34;&gt;Identifying a common task&lt;/h2&gt;\n&lt;p&gt;At the time of writing this, there are Tamil Nadu elections. I&amp;rsquo;ve made up my mind to ask the AI to visualize the TN elections and would rate them based on their accuracy and how beautifully they present the data.&lt;/p&gt;\n&lt;p&gt;Price-wise, Deepseek and the Chinese models are dominating the leaderboards for the price-to-perfomance ratio.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/may-2026/leaderboard-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/may-2026/leaderboard-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;deepseek&#34;&gt;Deepseek&lt;/h2&gt;\n&lt;p&gt;Though the design is okey, the accuracy is great.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/deepseek.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;glm&#34;&gt;GLM&lt;/h2&gt;\n&lt;p&gt;The design is great, especially where each seat is represented as an arc and color-coded, but the data is completely wrong.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/glm.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;qwen&#34;&gt;Qwen&lt;/h2&gt;\n&lt;p&gt;The design is mid, and the accuracy is also wrong.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/qwen.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion-winner&#34;&gt;Conclusion / Winner&lt;/h2&gt;\n&lt;p&gt;From the different conclusions, we have different results. Only the first model&amp;hellip; both the design and the model accuracy are good. Note that we have selected the expert mode.&lt;/p&gt;\n&lt;p&gt;In GLM&amp;hellip; the design is great without even the correct prompt&amp;hellip; but the accuracy is bad.&lt;/p&gt;\n&lt;p&gt;With this short test, I was able to figure out which AI I can rely on the most.&lt;/p&gt;\n&lt;p&gt;Results of AI:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/results-of-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Updated as of May 24th. Got to stay home more even though I had a prod release from the office (working at VZ at this point in time), but food poisoning and dehydration happened to my dad&amp;hellip; had to take him to hospital emergency care, but he recovered quickly. The marriage that is planned in May is in limbo; the partner wants some things to be finished from her side, and I&amp;rsquo;m feeling kinda the same on some matters. My SO is trying to find a good company to work for, in between managing family and me, hard for her as well. Take small steps forward; though it doesn&amp;rsquo;t make any sense at the current moment, in the long run, it will make sense.&lt;/p&gt;\n&lt;p&gt;Since COVID, I took medical insurance for my parents, both of them&amp;hellip; I didn&amp;rsquo;t think it would be of much use when my dad got admitted to the hospital&amp;hellip; but the hospital successfully processed it. In the end, I got the money I paid for the entire hospital visit, though others said they were useless (others meaning some of my brothers and others)&amp;hellip; I think it&amp;rsquo;s useful and provides some mental relief when something does happen in case of emergencies. I have insurance with the office that covers me but also my parents, but I&amp;rsquo;ve kept this separately. Thinking of taking separate health insurance and life insurance; don&amp;rsquo;t know what I&amp;rsquo;ve decided yet. I&amp;rsquo;ll let you know in the future what I decide.&lt;/p&gt;\n&lt;p&gt;Unrelated to this, GeForce Now got announced in India, and I got an invite for the trial. I stopped my gaming and those related activities recently, so I shared it with a friend of mine. He paired it with Xbox Game Pass and is having a good time with different games.&lt;/p&gt;\n&lt;p&gt;Got to meet some old friends that I didn&amp;rsquo;t meet in a long while; I felt human/grounded seeing the changes they have been through. I said this to one even: people are shaped by whom they interact with and whom they surround themselves with; they are sculpted subconsciously without them even realizing it&amp;hellip; seeing how differently they&amp;rsquo;ve changed.&lt;/p&gt;\n&lt;p&gt;The price of petrol has risen up so much&amp;hellip; right after the elections&amp;hellip; the politicians played their hand smartly&amp;hellip; in April 2026&amp;hellip; the price was around 99.82 rupees, and now it has been 106.xx rupees. Because of the ongoing conflict in the Middle East—specifically tensions surrounding the Strait of Hormuz disrupting global oil supply chains, the prices are expected to go even higher? I was planning to buy a good electric scooty, but then my friend&amp;rsquo;s dad said things to convince me not to take it (it was just before the war)&amp;hellip; now I&amp;rsquo;m feeling maybe I shouldn&amp;rsquo;t have brought it up with him, and as a result, wouldn&amp;rsquo;t have been influenced by him. I have to search for one electric scooty that&amp;rsquo;s good for the short term and also for the long term, considering the serviceability and all.&lt;/p&gt;\n&lt;p&gt;On top of that, I have to change the apartment in Chennai. I remember writing in January 2 years ago when I moved into this apartment. I&amp;rsquo;m thinking of moving back to my hometown and staying over at a friend&amp;rsquo;s place shortly&amp;hellip; until I&amp;rsquo;m done with a few things? Idk&amp;hellip; or move to a new apartment in Chennai so I can still stay there and commute to the office&amp;hellip; Idk, I&amp;rsquo;m conflicted. Maybe I should stay in a hotel for the days I&amp;rsquo;m going to the office. Argh! These are just simple decisions but take away valuable time and energy.&lt;/p&gt;\n&lt;p&gt;And lastly, it&amp;rsquo;s been one year, huh! If you know what I mean, I have to update this section later&amp;hellip; about what this is&amp;hellip; at a later point in time.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve started a long stagnated item; I made progress one way or another. Future me, how did it go through? Were you able to handle it? If you wanted to talk to someone about anything, I&amp;rsquo;m here with you; look back&amp;hellip; read these items and reminisce (atow - raining outside, thunderstorm, parents sleeping, open window, sofa near window, wind on the back of the head, midnight).&lt;/p&gt;</description></item><item><title>Offline AI models on Phones &amp; PC - April 2026</title><link>//bluepie.in/2026/04/april-2026/</link><pubDate>Sun, 12 Apr 2026 05:49:01 +0000</pubDate><guid>//bluepie.in/2026/04/april-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Today I want to talk about, &lt;a href=&#34;https://ai.google.dev/gemma/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Gemma 4&lt;/a&gt;; this sets a new standard for open source AI models. I am astonished by the quality of this, though it has a small model size compared to the other big ones out there.. like &lt;a href=&#34;https://kimi.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Kimi K2.5 from Moonshot AI&lt;/a&gt;, that thing is a 1 trillion parameter model.. 1 trillion! and Gemma 4&amp;rsquo;s biggest is only 31 billion.. but here is the thing, Kimi K2.5 only uses 32 billion of those parameters at any given time, so they are not that far apart in practice.. and Gemma 4 punches way above its weight for how small it actually is..&lt;/p&gt;\n&lt;h2 id=&#34;gemma4&#34;&gt;gemma4&lt;/h2&gt;\n&lt;p&gt;With the Gemma models, the naming conventions highlight their architecture, specifically using &lt;code&gt;A&lt;/code&gt; for &amp;ldquo;active&amp;rdquo; parameters and &lt;code&gt;E&lt;/code&gt; for &amp;ldquo;edge&amp;rdquo; or &amp;ldquo;efficient&amp;rdquo; models. For instance, &lt;code&gt;E2B&lt;/code&gt; and &lt;code&gt;E4B&lt;/code&gt; designate lighter models optimized for edge devices. They also utilize a Mixture of Experts (MoE) architecture, like the 26B MoE. In an MoE model, only a subset of the total parameters are &amp;ldquo;active&amp;rdquo; (&lt;code&gt;A&lt;/code&gt;) for any given token. Think of it like a restaurant with 26 chefs, but only 4 are actually cooking your order at any point while the rest stand by with their own specializations. This allows it to run as fast as a 4B model while retaining the extensive knowledge of a 26B one. On the other hand, there is the 31B Dense model. That one just loads everything—all 31 billion parameters fire for every single token. No routing, no tricks, just brute force. It uses much more memory, but its performance is more predictable.&lt;/p&gt;\n&lt;p&gt;There are two classifications of models, and in each of these classifications there are multiple models that are released. Two of the four.. are for desktop/laptop, and the others, they created for edge devices. What they mean by edge devices are mobile phones, Raspberry Pi, and other less powerful devices or mobile.&lt;/p&gt;\n&lt;h2 id=&#34;on-pc&#34;&gt;On PC&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been a long time user of &lt;a href=&#34;https://lmstudio.ai/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;lm studio&lt;/a&gt;, it offers great features.. I like it because it offers ease of use, ability to manage different models all at once,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/lm-studio-ui.webp&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;from the official documentation. I can run up to 14 GB of graphics memory worth of model. The 31B dense model needs around 20+ GB at full precision.. so that is out for me. The 26B MoE is interesting though.. even though it has 26B total params you only compute 4B at a time, so it is lighter on the processor, but you still have to load all 26B into VRAM which I don&amp;rsquo;t have.. at least until now. The trick people use is Q4 quantization, which basically compresses the model down significantly. The 31B at Q4 can fit around 16-17 GB.. still a bit above what I got but getting closer. Maybe next GPU.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-laptop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;on-mobile&#34;&gt;On mobile&lt;/h2&gt;\n&lt;p&gt;It runs surprisingly fast and remains fairly accurate. The output isn&amp;rsquo;t exactly the same as the desktop models, largely because of how mobile inference is optimized. Instead of performing full, heavy computations on every layer from scratch for every word, mobile execution uses techniques like quantization and KV caching. Essentially, the model reuses pre-calculated states (like a memory map) on each layer for the context it has already processed, which drastically speeds up generation on phones. Because of these aggressive optimizations, it&amp;rsquo;s hard to measure its accuracy strictly against the uncompressed desktop versions. Still, to give it credit, it handles an impressive 140 languages easily; heck, I even used my native language and it worked great when I tried it.&lt;/p&gt;\n&lt;p&gt;To run these models locally, I am using &lt;strong&gt;&lt;a href=&#34;https://github.com/google-ai-edge/gallery&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google AI Edge Gallery&lt;/a&gt;&lt;/strong&gt;. It is an open-source, experimental mobile application from Google that showcases running Large Language Models entirely on-device (Android and iOS). Because the app uses &lt;a href=&#34;https://ai.google.dev/edge/litert&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiteRT&lt;/a&gt; (formerly TensorFlow Lite) and the &lt;a href=&#34;https://ai.google.dev/edge/mediapipe/solutions/genai/llm_inference&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LLM Inference API&lt;/a&gt;, the models execute offline directly on the phone&amp;rsquo;s hardware. This means complete data privacy with no cloud API costs. The app provides great features out of the box like LLM Chat, multimodal interactions to ask questions about images, a Prompt Lab for testing ideas locally.. and much more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-mobile.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal&#34;&gt;personal&lt;/h2&gt;\n&lt;p&gt;Hey, how are you doing? I hope everything is going well. I lost my iPhone this month; it was hectic. I lost it when traveling back from the office to my Chennai home. Although I have had the phone for a while now, and it&amp;rsquo;s old.. it still stings. My first time losing a phone. Why would I lose an iPhone? It was a series of events that happened that day. A few days before.. I accidentally bent the new glasses I got. Then during that day, the glasses got so loose that I couldn&amp;rsquo;t wear them the entire day. The temples (new term.. weird to call that piece that.. anyway) were so loose, they would have come into two pieces if I had worn them that day. So before that, a day ago, my mom got into a little accident. All of these in mind, I got into a weird headspace and it led to me losing the phone. I felt something drop.. but when I looked for it, it was still there where I kept the phone. On top of it, the pouch that I kept the phone in was torn a bit and I hadn&amp;rsquo;t changed it.. which also contributed to this. I&amp;rsquo;m never keeping a phone in a pouch again! ..that&amp;rsquo;s what I told myself, but future me will catch myself doing the same in the future, I think. After that.. I lost my building entrance card. My office card I had, but I lost the Olympia Guindy building card, which is another pain.. but at the time of updating this, on April 15, it was found and I went and collected it.&lt;/p&gt;\n&lt;p&gt;It&amp;rsquo;s been a long time since I bought a phone. Before, I used to develop for Android and iOS; now I don&amp;rsquo;t anymore. I don&amp;rsquo;t think I need an iPhone again.. a better deal is to buy a latest &lt;a href=&#34;https://www.qualcomm.com/snapdragon&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Snapdragon chipset&lt;/a&gt; phone and call it a day. Going to switch back to bigger XL screen phones.&lt;/p&gt;\n&lt;p&gt;Lots of things to do, yet you choose to have little moments of fun? At the moment, my brain can&amp;rsquo;t remember what pending things are left.&lt;/p&gt;\n&lt;p&gt;A friend stayed over for a while at the start of April. I had to work on weekends because of horrible management in the office. I try to do a small piece of work every day though, but still.. yeah. He was going on a trip.. north and back and stayed for a while. The initial plan was that he would stay here for a while, but then we would both go to our hometown. He is a childhood friend of mine, but work got hectic; I couldn&amp;rsquo;t make it as discussed next week and he had to go alone.&lt;/p&gt;\n&lt;p&gt;Last year at this time, I was playing &lt;a href=&#34;https://www.playstation.com/games/ghost-of-tsushima/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Ghost of Tsushima&lt;/a&gt; in my off-time. This year, I&amp;rsquo;m playing &lt;a href=&#34;https://megacrit.com/slaythespire2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Slay the Spire 2&lt;/a&gt; and &lt;a href=&#34;https://crimsondesert.pearlabyss.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Crimson Desert&lt;/a&gt; (released on March 19th, 2026) in my off-time.. dang nab it, I should give up this gaming.. I&amp;rsquo;m getting old.. but what if that changes me entirely? Risky..&lt;/p&gt;</description></item><item><title>&#39;AI Skills&#39;</title><link>//bluepie.in/2026/03/mar-2026/</link><pubDate>Tue, 03 Mar 2026 05:24:25 +0000</pubDate><guid>//bluepie.in/2026/03/mar-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Anthropic came and published their skills documentation on what they are roughly 2 months ago, but I suspect they and other AI companies were using them internally even before they published. The idea of people seeing them as a new thing and being astonished is abysmal..because we were already using it..but without the naming convention and the optimised context limiting. The concept is simple: you create a folder named skill you want, then inside it, the main file is called skill.md. This takes less context when ai reads it, as it just reads the frontmatter alone; you write detailed instructions below the frontmatter with different scripts and whatever that is necessary to finish that task/skill, you also writter a condition upon which the ai will trigger. The AI then keeps all these details and uses it ad hoc. The problem it solves is that this uses way less context than mcp or others and only triggers when necessary.&lt;/p&gt;\n&lt;h2 id=&#34;example-plantuml-generator&#34;&gt;Example: PlantUML Generator&lt;/h2&gt;\n&lt;p&gt;So the idea is simple; as you can infer from above, let&amp;rsquo;s go with an example. I wanted to create an architecture diagram skill in office. ..in office we use he PlantUML to create sequence diagrams.&lt;/p&gt;\n&lt;p&gt;The problem is you have to ask the AI to generate it, and then generate the image and maintain the image somewhere. The problem with this is it takes time and its a manual process. How can we use skills to automate this? We can write instructions to ask the AI chat / agent to be like, if a user asks x, then do the process and update the diagram. We can add these instructions in the skill.md file, keep the name and descriptions to work with the architecture diagram.&lt;/p&gt;\n&lt;p&gt;You can write scripts and then use them to generate things; the AI will execute them based on the instructions. The next is dependencies, if any. Since PlantUML is an open-source project, so we can download the jar file and put that in the skills folder. since its a java project..it&amp;rsquo;s platform independent, so no need to worry about different binaries&lt;/p&gt;\n&lt;p&gt;So everything can be packaged into a single skill.&lt;/p&gt;\n&lt;p&gt;you can see the skills folder here: &lt;a href=&#34;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;embedding-code-in-images&#34;&gt;Embedding Code in Images&lt;/h2&gt;\n&lt;p&gt;On top of the skill, I was thinking about for a optimization. In college, I conducted a Capture the Flag; the event was like Google CTF and other CTF events from that time. I was inspired by them, Anyway. one of the competitions was to find the hidden details from the image. Inside an image, you can add a data to it, it can be any data. So I was thinking, why can&amp;rsquo;t we use the same thing here? So I asked ai to write a script to embed code into the images. So the PlantUML diagram is stored inside the images, the images are the output of the code. It&amp;rsquo;s like a chicken and egg problem once you delete the code file from the repo.&lt;/p&gt;\n&lt;p&gt;We want crud options on this; the update image, edit image, and create image are just a set of instructions that the AI can read and execute.. in my case it will be Python scripts, when user asks to edit, it will extract the code from the image and create a temporary file we can work on, and when we ask to store it, it saves the source code, again inside the image, deleting the code file.&lt;/p&gt;\n&lt;h2 id=&#34;other-skills&#34;&gt;Other Skills&lt;/h2&gt;\n&lt;p&gt;There are other skills that I use here, it keeps changing overtime.&lt;/p&gt;\n&lt;p&gt;The main one I wanted to share is the PlantUML generator; it has all the features that I need to be adding to demonstrate the features of the skill.&lt;/p&gt;\n&lt;h2 id=&#34;sub-agents-lifecycle-hooks&#34;&gt;Sub-agents &amp;amp; Lifecycle Hooks&lt;/h2&gt;\n&lt;p&gt;At the end of Feb 2026, the Claude team updated the skillset to have custom features, like sub-agents. What it is, is that the sub-agent is being called by the main agent when doing a certain task.. so it will fix the context problem. it&amp;rsquo;s one of the major updates to skills; it&amp;rsquo;s the major one that makes a significant difference, because it won&amp;rsquo;t clutter the main context; the main agent is not interfered with.&lt;/p&gt;\n&lt;p&gt;It also has access to the lifecycle events, Lifecycle hooks (on_start, on_success, on_error, PreToolUse, etc.).. but i haven&amp;rsquo;t adopted them yet; but this will help me with the cryo vault project: &lt;a href=&#34;https://github.com/aghontpi/cryo-vault&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/cryo-vault&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m having to juggle a lot of things in my life currently; finding focus time to think on what is essential or next is hard with this attention-seeking world; socializing; maintaining health and ad hoc new plans; argh! I started to have a new habit; hopefully, when I am consistent on this.. I&amp;rsquo;ll post on this later!.. future me..! what is happening? hope you are good!&lt;/p&gt;</description></item><item><title>Selfhosting an Automation tool - N8N</title><link>//bluepie.in/2026/02/feb-2026-2/</link><pubDate>Sat, 28 Feb 2026 15:17:41 +0000</pubDate><guid>//bluepie.in/2026/02/feb-2026-2/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;In the previous post, I was talking about &lt;a href=&#34;https://claude.com/skills&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;claude&lt;/a&gt; skills. This is about automation, no code/ minimal solutions. At this point, it&amp;rsquo;s been over 1.5 to 2 years since &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and automation; nowadays it&amp;rsquo;s easy to just build an app/ ask AI to build the app instead of creating the automation, it&amp;rsquo;s easier that way, but regardless, let&amp;rsquo;s see how the future goes; I&amp;rsquo;m already seeing the trend of creating custom apps for each one&amp;rsquo;s personal preference.&lt;/p&gt;\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;Instead of creating projects for each use and hosting it for every use case, I thought of using automation tools and solving those problems; for ex, one such problem is creating a diet maintainer; I ranted in a previous post that I gained a couple of kgs in a short while, but I&amp;rsquo;m trying to reduce it; so I have to keep track of the amount of calories, etc., but I want this to be an Excel sheet for tracking, yes there are apps for that; but I wanted a simple chat interface where I can type the food I ate and when I ate it, and it should approximate the calories, and when I ask how much is left, it should give back the amount of food that I can eat in the remaining part of the day/ from that point onwards.&lt;/p&gt;\n&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;\n&lt;p&gt;I choose n8n. The main reason I prefer it is the immense community support it has, it&amp;rsquo;s incredibly popular, which means if I run into a wall, someone else has probably already solved it and posted about it. Plus, being able to tinker with it under the hood while self-hosting is a massive bonus compared to locked-in cloud alternatives.&lt;/p&gt;\n&lt;p&gt;Speaking of alternatives, there are quite a few out there:\n- &lt;a href=&#34;https://www.make.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Make (formerly Integromat)&lt;/a&gt;: Great visual builder,\n- &lt;a href=&#34;https://zapier.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Zapier&lt;/a&gt;: The biggest name out there with the most integrations,\n- &lt;a href=&#34;https://pipedream.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Pipedream&lt;/a&gt;: Fantastic for developers who want to write code snippets, but maybe overkill if you just want pure drag-and-drop.\n- &lt;a href=&#34;https://www.activepieces.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Activepieces&lt;/a&gt;: is similar to n8n&lt;/p&gt;\n&lt;h2 id=&#34;self-hosting&#34;&gt;Self hosting&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Here is the official n8n documentation guide&lt;/a&gt;, I started to see the official docs of n8n. One thing I got surprised by is that they have incorporated AI, and that AI is surprisingly good at providing the right instructions, because when you host with different servers, server config differs for each one like &lt;a href=&#34;https://caddyserver.com/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;caddy&lt;/a&gt; / &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;/ etc., and for automation to work, it has to be able to receive webhooks, and alongside things like if it&amp;rsquo;s behind a proxy; it must be configured to tell that you are behind a proxy, and based on that it can handle the incoming request, etc.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll save you the trouble and give the config here; basically I was able to set up the Nginx server pretty easily using the AI bot that they have incorporated in their site. Below is all the config I needed to be able to run this from &lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; and &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;:&lt;/p&gt;\n&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I like to run things with &lt;a href=&#34;https://docs.podman.io/en/latest/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;podman&lt;/a&gt;/&lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; as it removes the dependency from my cloud machine.&lt;/p&gt;\n&lt;p&gt;Obviously I have to configure my domain name to &lt;a href=&#34;https://www.godaddy.com/help&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;godaddy&lt;/a&gt;/&lt;a href=&#34;https://developers.cloudflare.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt; and deploy SSL certificates; I&amp;rsquo;ve covered that plenty in my other posts:&lt;/p&gt;\n&lt;p&gt;docker config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;podman run -d --restart unless-stopped \\\n--name n8n \\\n-p 5678:5678 \\\n-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \\\n-e N8N_RUNNERS_ENABLED=true \\\n-e WEBHOOK_URL=https://&amp;lt;server-name&amp;gt;/ \\\n-e N8N_PROXY_HOPS=1 \\\n-e N8N_HOST=&amp;lt;server-name&amp;gt;\n-e N8N_PORT=5678 \\\n-e N8N_PROTOCOL=https \\\n-v /mnt/podman-data/n8n_data:/home/node/.n8n:Z \\\ndocker.n8n.io/n8nio/n8n\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;nginx config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;server {\nlisten 80;\nserver_name &amp;lt;server-name&amp;gt;\nlocation / {\nproxy_pass http://127.0.0.1:5678;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_set_header X-Forwarded-Port $server_port;\nchunked_transfer_encoding off;\nproxy_buffering off;\nproxy_cache off;\n}\nlocation /webhook/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\nlocation /webhook-test/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-how-to-configure&#34;&gt;Configuring - How to configure&lt;/h2&gt;\n&lt;p&gt;Modifying a telegram bot to track the calory intake of the day based on estimates from me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/overview.png&#34; alt=&#34;Overview&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is initial learning time to get used to what each option is, but when you get the hang of it, it&amp;rsquo;s pretty easy, since I&amp;rsquo;ve done drag and drop editors before; I caught up easily in approx 5-10ish mins.&lt;/p&gt;\n&lt;p&gt;Create a &lt;a href=&#34;https://core.telegram.org/bots/api&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;telegram&lt;/a&gt; node and select the appropriate Telegram account credential. Set the &lt;strong&gt;Trigger On&lt;/strong&gt; parameter to &lt;code&gt;Message&lt;/code&gt; so it triggers on new incoming messages of any kind (text, photo, sticker, etc.). Note that due to Telegram API limitations, you can only use one Telegram trigger per bot. Also ensure there is validation to only process messages from your known user ID, otherwise the bot will refuse to respond.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/telegram-trigger-receive.png&#34; alt=&#34;Telegram Trigger Settings&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Add an &lt;strong&gt;Analyze Text Message&lt;/strong&gt; node and connect it to your Google Gemini (or equivalent API) configuration. Set the Resource to &lt;code&gt;Text&lt;/code&gt; and Operation to &lt;code&gt;Message a Model&lt;/code&gt;, using the &lt;code&gt;models/gemini-2.5-flash&lt;/code&gt; model so its fast and reliable. models cheaper than 2.5-flash are not reliable based on testing. Within the node, establish a system prompt (Role: &lt;code&gt;Model&lt;/code&gt;) instructing the AI to process user input into structured nutritional data—identifying meal components, estimating portion weights, and warning it not to reveal its internal steps. Pass the user input as a prompt (Role: &lt;code&gt;User&lt;/code&gt;) using the expression &lt;code&gt;{{ $json.message }}&lt;/code&gt; along with today&amp;rsquo;s date context: &lt;code&gt;{{ $today.format(&#39;yyyy-MM-dd&#39;) }}&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/using-gemini-ai-via-api.png&#34; alt=&#34;AI Processing Configuration&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;To store the parsed responses, add a Google Sheets node configured to &amp;ldquo;Append&amp;rdquo; rows, and set it to &lt;strong&gt;Map Each Column Manually&lt;/strong&gt;. You can map fields originating from the trigger and model response:\n- &lt;strong&gt;User_ID&lt;/strong&gt;: &lt;code&gt;{{ $json.chat_id }}&lt;/code&gt;\n- &lt;strong&gt;Date&lt;/strong&gt;: Extract and format the date directly with &lt;code&gt;{{ DateTime.fromSeconds($json.date).format(&#39;yyyy-LL-d&#39;) }}&lt;/code&gt;.\n- Dietary metrics like &lt;strong&gt;Meal_description&lt;/strong&gt;, &lt;strong&gt;Calories&lt;/strong&gt;, &lt;strong&gt;Proteins&lt;/strong&gt;, &lt;strong&gt;Carbs&lt;/strong&gt;, and &lt;strong&gt;Fats&lt;/strong&gt; can be defined automatically by mapping them from structured outputs generated by the AI model.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/append-to-excel-sheet.png&#34; alt=&#34;Append to Google Sheets&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is all sorts of existing nodes (in &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;, a &amp;ldquo;node&amp;rdquo; is basically a building block or a step in your automation workflow—it can be a trigger that starts the flow, an action that connects to a specific app&amp;rsquo;s API like sending a message, or a logic step that filters or processes data) that we can use. For this use case, reusing the &lt;a href=&#34;https://developers.google.com/sheets/api/guides/concepts&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google sheet&lt;/a&gt; node from official requires getting API keys, but that is well documented by &lt;a href=&#34;https://docs.n8n.io/integrations/builtin/credentials/google/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and &lt;a href=&#34;https://developers.google.com/workspace/guides/create-credentials&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;Finally, configure another Telegram node to send a reply back to the user. Set the Resource to &lt;code&gt;Message&lt;/code&gt; and Operation to &lt;code&gt;Send Message&lt;/code&gt;. For the &lt;strong&gt;Chat ID&lt;/strong&gt;, retrieve it from the initial trigger using &lt;code&gt;{{ $(&#39;Telegram Trigger&#39;).item.json.message.chat.id }}&lt;/code&gt;. Include the formatted Text from the AI calculation &lt;code&gt;{{ $json.message }}&lt;/code&gt;. Under Additional Fields, be sure to set &lt;strong&gt;Parse Mode&lt;/strong&gt; to &lt;code&gt;MarkdownV2&lt;/code&gt; so any formatting from the AI looks correct, and toggle off &amp;ldquo;Append n8n Attribution&amp;rdquo; for a cleaner message.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/send-telegram-msg-back.png&#34; alt=&#34;Send Telegram Summary Message&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For the initial setup, because it&amp;rsquo;s new, I was experimenting and took 4-5 hours, because it was interesting; but been using that for maintaining calories now; it&amp;rsquo;s great.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;AI is more prevalent these days, instead of building these automation no code solutions via these tools, users directly build with AI, there are pros and cons.. it&amp;rsquo;s already deployed on the server (the automation tool, one time setup), can deploy any number of times.. limit depends on the server limits, I&amp;rsquo;m okay.. but people need to know of how to debug in case of error, I mean there is AI for that too; if using &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;/automation.. everything is contained within this little confinement work area.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll be trying out other automation experiments, this is serving me well, recently I gained a lot of weight, though not overweight.. I still look fit from an outside view; but a little fat in stomach and back.. I started eating snacks every night and lots of oats (used to prepare the previous night with milk and sugar.. keep it in freezer).. the next morning eating it, I drove this diet for some time, it was good for my morning routine (not going to office on an empty stomach), but the problem was I gained like 5 kgs, I have never been this much weight in my entire life! but I&amp;rsquo;m 6ft.. so the weight is kinda explainable.&lt;/p&gt;\n&lt;p&gt;Me when 76kgs, ideal for my height, less fat content ratio in my body; this is before I got muscle buildup.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/me-when-76.jpg&#34; alt=&#34;image when I was 76kgs&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;but in terms of physique, , I got visibly more muscle development..after above image, idk.. started to go to gym again, let&amp;rsquo;s see, till now I&amp;rsquo;ve been continuing to do it every day except Sunday at home. But I&amp;rsquo;ll track the progress.&lt;/p&gt;\n&lt;p&gt;Updating this part as of March 2026, it&amp;rsquo;s happening again, I feel out of place, everyone around here is behind.. so behind .. than I expect them to be in, when I see, I see a fine line between things and it&amp;rsquo;s like I can slip in between them.. and go past beyond. I see it like this, a highway road that is busy with vehicles going left and right, but I see a path in slow motion that I can slip in between and move to the other side, I see what others are not seeing, IDK how long I will be blessed with this gift or curse that I have, can&amp;rsquo;t share this with anyone, I exposed this lil bit to sq#3, ~Future me is this happening again? Hope you landed on things, learned from past.&lt;/p&gt;</description></item><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item></channel></rss>"}, "83fcac3b66d93f44a532dda7632ef535ca219ef2": {"t": 1785770726.0578814, "n": 160942, "h": "07c723a6bd3fedcc", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Thu, 18 Jun 2026 21:50:43 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>June 2026 - Deepseek API For Performance &amp; Cost</title><link>//bluepie.in/2026/06/june-2026/</link><pubDate>Thu, 18 Jun 2026 21:50:43 +0000</pubDate><guid>//bluepie.in/2026/06/june-2026/</guid><description>\n&lt;blockquote&gt;\n&lt;p&gt;(following post has been updated 5th of july, 2026)&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;intro&#34;&gt;Intro&lt;/h3&gt;\n&lt;p&gt;A shift in LLM/AI usage: GitHub Copilot changed its user pricing. Microsoft changed the pricing. Nvidia &amp;amp; AMD are coming up with new laptops with 128 or more GB of VRAM to run agents and LLMs locally! But the prices of the chips are not going down; at the time of writing, the Nvidia RTX 5070 16GB is 65k INR.&lt;/p&gt;\n&lt;p&gt;The company that I work for at the time of writing this broke the Microsoft GitHub Copilot tie-up and looked for other vendors. They did it so fast: they sent a company-wide email stating it would be terminated immediately for the FTE (own employees) developers but will be kept only for the vendors, and asked users to utilize and adopt Claude Code. So we started using that, but a lot of people just use it for the sake of it to finish the job. At this point, there is no auto mode in the Claude version they allow inside the organization; people just set the model to high and burn through the tokens. It&amp;rsquo;s been 2 days and people went ham on it, and already 25% of the team budget is gone. We can check out individual budgets and the team&amp;rsquo;s budget at the usage metrics.&lt;/p&gt;\n&lt;p&gt;For me, I got around 300 dollars of AI credits at my disposal: $100 for Claude and around 200 for Cursor. To think that is around 28k INR—the salary I got at the time of starting my career when I left college. Back then, was it considered good money? Things changed really fast and got pricey, huh? These token budgets are arbitrary values set by companies on their own terms and bullshit logic. We are paying money and allocating budget for thin air—tokens? A decade ago, people would laugh at the idea! That would be 2016. I remember 2016; it was a different time: peaceful, chaotic, it had its charms.&lt;/p&gt;\n&lt;h2 id=&#34;deepseek-api-for-the-win&#34;&gt;Deepseek api For The Win&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using the DeepSeek API directly for the entire month of June 2026. It&amp;rsquo;s been great; they offer cheap prices for the latest model they have (at the point of writing this in June/July 2026 (this line was edited on July 5th, 2026, 1:08 AM)). Here is a comparison of the API pricing per 1 million tokens (as of July 2026):&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th align=&#34;left&#34;&gt;Provider&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Model&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Input Price (Cache Miss)&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Input Price (Cache Hit)&lt;/th&gt;\n&lt;th align=&#34;left&#34;&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;DeepSeek&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;deepseek-v4-flash&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.14&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.0028&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.28&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;deepseek-v4-pro&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.435&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.003625&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.87&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Google&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Gemini 2.5 Flash-Lite&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.10&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;—&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.40&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Gemini 3.5 Flash&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$1.50&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;—&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$9.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Gemini 3.1 Pro Preview&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$2.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;—&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$12.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;Anthropic&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Claude Haiku 4.5&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$1.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.10 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$5.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Claude Sonnet 5&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$3.00*&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.30 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$15.00*&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;Claude Opus 4.8&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$5.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.50 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$25.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;strong&gt;OpenAI&lt;/strong&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;GPT-5.4 Nano&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.20&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.02 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$1.25&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;GPT-5.4 (Production)&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$2.50&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.25 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$15.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;&lt;code&gt;GPT-5.5 (Flagship)&lt;/code&gt;&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$5.00&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$0.50 (90% off)&lt;/td&gt;\n&lt;td align=&#34;left&#34;&gt;$30.00&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;em&gt;*Note: Claude Sonnet 5 features introductory pricing of $2.00/$10.00 per million tokens through August 31, 2026. For verification, check the official docs: &lt;a href=&#34;https://platform.deepseek.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;DeepSeek Pricing&lt;/a&gt;, &lt;a href=&#34;https://aistudio.google.com/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google AI Studio Pricing&lt;/a&gt;, &lt;a href=&#34;https://www.anthropic.com/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Anthropic Claude Pricing&lt;/a&gt;, and &lt;a href=&#34;https://openai.com/api/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;\n&lt;h2 id=&#34;deepseek-api-for-the-right-reason&#34;&gt;Deepseek api for the right reason.&lt;/h2&gt;\n&lt;p&gt;All the frontier AI models at this point are capable of doing the job they are told to do. There is a saying that you just have to do 80%/90% of the work, and the remaining work I can do myself. A lot of people are feeling that way regarding the AI models. What about them? The AI models have been performing well over the last 6 months; we don&amp;rsquo;t want the last itty-bitty performance for the day-to-day tasks that we do. It is needed for hard problems, sure, but for daily things it&amp;rsquo;s definitely not needed. So, for the reason of being cheap, affordable, and open weights&amp;hellip; I chose to use DeepSeek.&lt;/p&gt;\n&lt;h2 id=&#34;purpose&#34;&gt;Purpose&lt;/h2&gt;\n&lt;p&gt;I mainly started to use the deepseek api for the coding, they offer the model to be compatible with claude code and vscode (github copilot), they have instructions to integrate with every tool you can think of — see the &lt;a href=&#34;https://api-docs.deepseek.com&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;DeepSeek API Documentation&lt;/a&gt; for details. Specifically, for VS Code you can use extensions like &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=Continue.continue&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Continue&lt;/a&gt; or &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Cline&lt;/a&gt; configured to point to the base URL &lt;code&gt;https://api.deepseek.com&lt;/code&gt;, or install the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=deepseek.deepseek-v4-copilot-chat&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;DeepSeek V4 for Copilot Chat&lt;/a&gt; extension. For Claude Code, you can use the Anthropic-compatible endpoint by exporting environment variables before starting the CLI:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;export ANTHROPIC_BASE_URL=&amp;quot;https://api.deepseek.com/anthropic&amp;quot;\nexport ANTHROPIC_AUTH_TOKEN=&amp;quot;&amp;lt;your_deepseek_api_key&amp;gt;&amp;quot;\nexport ANTHROPIC_MODEL=&amp;quot;deepseek-v4-pro[1m]&amp;quot;\nexport ANTHROPIC_DEFAULT_OPUS_MODEL=&amp;quot;deepseek-v4-pro[1m]&amp;quot;\nexport ANTHROPIC_DEFAULT_SONNET_MODEL=&amp;quot;deepseek-v4-pro[1m]&amp;quot;\nexport ANTHROPIC_DEFAULT_HAIKU_MODEL=&amp;quot;deepseek-v4-flash&amp;quot;\nexport CLAUDE_CODE_SUBAGENT_MODEL=&amp;quot;deepseek-v4-flash&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;No matter how much I use, they didn&amp;rsquo;t utilize the money I topped up on their site. It was just $5, but the usage I was doing was hardly making a dent. Given that I was using the pro model for the tasks and the flash model for menial tasks, seeing that it made no difference on the token budget, I started to use the DeepSeek-V4-Pro model for all tasks. Even then&amp;hellip; it made no dent in the billing. I still haven&amp;rsquo;t utilized the $5.&lt;/p&gt;\n&lt;p&gt;I was mostly using it for coding and simple tasks like automation to do mundane tasks, but just for the one instance, that&amp;rsquo;s it. Since I was doing coding mostly for experimental stuff and not code that I want to be private, I didn&amp;rsquo;t care about the Chinese company using my prompts or data. It&amp;rsquo;s been an agreed-upon truth (for anyone reading from far beyond~ maybe a different era), (everybody in the world agrees upon this one thing) that all the Chinese companies ever do is steal technology and steal data from people around the world. At the point of writing this, maybe things will have changed in the future. There are other service providers that advertise/say they don&amp;rsquo;t train on the API we use (like OpenCode (I&amp;rsquo;m going to try that next month, I&amp;rsquo;ll tell you why in a bit)); they also host the DeepSeek API inside the USA. Since the model is open-weights&amp;hellip; they can just download and host them on their own hardware&amp;hellip; or buy it from vendors inside the USA and serve it to customers, but internally they could be doing anything we don&amp;rsquo;t know.&lt;/p&gt;\n&lt;h2 id=&#34;evolving&#34;&gt;Evolving&lt;/h2&gt;\n&lt;p&gt;As I&amp;rsquo;ve stated before, since there is no cost involved for most of the time when I&amp;rsquo;m using the APIs, I started to use AI agents &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt;. Before, I just used them whenever necessary with different tools, but never ran Hermes or OpenClaw for a long time. I started to use OpenClaw and the Hermes agent on my laptop. Then, liked it? I made the setup on my server. Now, both the Hermes agent and OpenClaw are running &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; on my server. At first, it was just intrigue: why did others get obsessed with them? But since the cost is low, why not? And I tried to understand why people were hyping it up, and I understood to some extent. If I had it on my laptop and running all the time&amp;hellip; I don&amp;rsquo;t know how I would have felt, but running it on my server all the time is a different story. But I only gave it restricted access, and it&amp;rsquo;s running inside a container&amp;hellip; even inside that container, it doesn&amp;rsquo;t have sudo access.&lt;/p&gt;\n&lt;p&gt;Initially, I liked OpenClaw, but then when I used it more and more, I started to use Hermes more. It is important to note that OpenClaw is written in Node and uses Node for everything when doing tasks, but Hermes is written in Python, so it uses Python everywhere when it&amp;rsquo;s doing tasks.&lt;/p&gt;\n&lt;p&gt;Since there is no cost, I started to use AI agents &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; (Hermes and OpenClaw). Before, I just used them whenever required, so I started experimenting. Instead of just on my laptop, I hosted them on the server, configured 3 messaging apps, and added others. They work nicely and actually save time mostly by doing work independently.&lt;/p&gt;\n&lt;h2 id=&#34;how-much-ai-tokens-did-i-spend-this-month-from-deepseek-directly-updated-after-june&#34;&gt;How much AI tokens did I spend this month from DeepSeek directly? (updated after June)&lt;/h2&gt;\n&lt;p&gt;So for the whole month, since I started using it, I&amp;rsquo;ve used 400M tokens and around 10$, i&amp;rsquo;ll cover in the next post of what i&amp;rsquo;be been using this for.. hint (hermes &amp;amp; openclaw)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2026/deepseek-uage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s 3:26 AM on a night. I&amp;rsquo;m pondering over things, thinking through things. Things are muddy during the day; I&amp;rsquo;m thinking how can I get this calmness during the day&amp;hellip; silence? The comfort of knowing others are asleep, only I am awake, and no one&amp;hellip; willingly at this hour contacts anyone. I&amp;rsquo;ve gotta find that cheat. Is it the ambiance? Or the feeling of knowing there is still 7 hours of time left&amp;hellip; until I won&amp;rsquo;t be bothered by other things.&lt;/p&gt;\n&lt;p&gt;This month, I&amp;rsquo;m doing something that I wanted to do for a long time: moving out of Chennai and back to my hometown with all my things and stuff. I&amp;rsquo;ve been thinking&amp;hellip; &amp;ldquo;what if&amp;rdquo; this and that for a long time&amp;hellip; but the thing is&amp;hellip; it&amp;rsquo;s good to be prepared. I&amp;rsquo;ve realized I can just do it again when it&amp;rsquo;s required; it&amp;rsquo;s not a big deal. Sure, it can be based on luck, but I&amp;rsquo;m sure I have talented people and smart SAs to sort it out. It&amp;rsquo;s been a long time&amp;hellip; I have an attachment to this place now—my messy table, AC at 21 degrees C on a July night, lights on, curtains half open, fan on top of my head at 55% with rainy weather outside in Chennai and a cold night. Remember this: no glasses, closet behind, half asleep but yet wide awake, thoughts of wanting to sleep so I won&amp;rsquo;t be groggy for work next morning, reflecting, searing memory, what-ifs.\nSorry, all over the place. Swear I&amp;rsquo;m not drunk.&lt;/p&gt;\n&lt;p&gt;Still double-minded. Maybe I should keep a small place for myself alone? On the fence on this. All the actions I take based on this&amp;hellip; are fruitless. I have the weekend to disassemble things; I think it would be a different set of tasks than I usually do. Gosh, to think I was doing a lot of different things 2 and 4 years ago on the weekends than what I am doing currently.&lt;/p&gt;\n&lt;p&gt;What happened to EV S, H, M? What happened to those rules of L? Can&amp;rsquo;t control how and what you are already perceived as. Best to get rid of it, stepping stone, learn?&lt;/p&gt;\n&lt;p&gt;From me talking earlier about GPUs, I&amp;rsquo;m thinking of building a machine just for running desktop-level LLMs. For most not-so-significant knowledge work, we don&amp;rsquo;t need a state-of-the-art model with advanced reasoning. The things people require are context-aware agents/AI that follow the instructions and act accordingly&amp;hellip; that&amp;rsquo;s all there is to it, at least at the current state of LLMs now. All the open-weights models have gotten a lot better over the months; we can host the flash version of those models easily and we are good. But I think I would need a desktop-grade/level GPU&amp;hellip; multiple GPUs, to have that sort of resource available to me. In the future, there are probably cheaper ways to run LLMs, or even better, LLMs won&amp;rsquo;t require GPUs. There are already TPUs from Google and NPUs&amp;hellip; but still the same as a GPU in the future&amp;hellip; or better yet, there are no LLMs at all? AI is completely different.&lt;/p&gt;\n&lt;p&gt;After so long, I left my Chennai apartment. I remember writing about it 2 years ago on a December, now on a June. I&amp;rsquo;m planning to vacate this month; I already informed them that I would be leaving. Will update on this next month.&lt;/p&gt;\n&lt;!-- maybe I&#39;m just angry at myself for the things I&#39;ve done in my life, it&#39;s not that great? To send in Discord, I stopped saying things but that takes a lot of time, so conserving. When did this change? --&gt;\n&lt;!-- only thing that I have is my studies and my self-work, never give that up for anything, remember the dread, 1:04 AM, can&#39;t keep up doing the same thing --&gt;</description></item><item><title>Comparing Free Chinese Models For Daily Use - May 2026</title><link>//bluepie.in/2026/05/may-2026/</link><pubDate>Fri, 01 May 2026 17:54:17 +0000</pubDate><guid>//bluepie.in/2026/05/may-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I wanted to test the performance and accuracy of the ai models at the time of writing this (May 2026, so I can leverage them in my day-to-day tasks).\nCurrently, I am testing 3 models: 1. Deepseek (v4 just got released) 2. GLM 3. Qwen.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-a-common-task&#34;&gt;Identifying a common task&lt;/h2&gt;\n&lt;p&gt;At the time of writing this, there are Tamil Nadu elections. I&amp;rsquo;ve made up my mind to ask the AI to visualize the TN elections and would rate them based on their accuracy and how beautifully they present the data.&lt;/p&gt;\n&lt;p&gt;Price-wise, Deepseek and the Chinese models are dominating the leaderboards for the price-to-perfomance ratio.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/may-2026/leaderboard-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/may-2026/leaderboard-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;deepseek&#34;&gt;Deepseek&lt;/h2&gt;\n&lt;p&gt;Though the design is okey, the accuracy is great.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/deepseek.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;glm&#34;&gt;GLM&lt;/h2&gt;\n&lt;p&gt;The design is great, especially where each seat is represented as an arc and color-coded, but the data is completely wrong.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/glm.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;qwen&#34;&gt;Qwen&lt;/h2&gt;\n&lt;p&gt;The design is mid, and the accuracy is also wrong.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/qwen.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion-winner&#34;&gt;Conclusion / Winner&lt;/h2&gt;\n&lt;p&gt;From the different conclusions, we have different results. Only the first model&amp;hellip; both the design and the model accuracy are good. Note that we have selected the expert mode.&lt;/p&gt;\n&lt;p&gt;In GLM&amp;hellip; the design is great without even the correct prompt&amp;hellip; but the accuracy is bad.&lt;/p&gt;\n&lt;p&gt;With this short test, I was able to figure out which AI I can rely on the most.&lt;/p&gt;\n&lt;p&gt;Results of AI:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/results-of-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Updated as of May 24th. Got to stay home more even though I had a prod release from the office (working at VZ at this point in time), but food poisoning and dehydration happened to my dad&amp;hellip; had to take him to hospital emergency care, but he recovered quickly. The marriage that is planned in May is in limbo; the partner wants some things to be finished from her side, and I&amp;rsquo;m feeling kinda the same on some matters. My SO is trying to find a good company to work for, in between managing family and me, hard for her as well. Take small steps forward; though it doesn&amp;rsquo;t make any sense at the current moment, in the long run, it will make sense.&lt;/p&gt;\n&lt;p&gt;Since COVID, I took medical insurance for my parents, both of them&amp;hellip; I didn&amp;rsquo;t think it would be of much use when my dad got admitted to the hospital&amp;hellip; but the hospital successfully processed it. In the end, I got the money I paid for the entire hospital visit, though others said they were useless (others meaning some of my brothers and others)&amp;hellip; I think it&amp;rsquo;s useful and provides some mental relief when something does happen in case of emergencies. I have insurance with the office that covers me but also my parents, but I&amp;rsquo;ve kept this separately. Thinking of taking separate health insurance and life insurance; don&amp;rsquo;t know what I&amp;rsquo;ve decided yet. I&amp;rsquo;ll let you know in the future what I decide.&lt;/p&gt;\n&lt;p&gt;Unrelated to this, GeForce Now got announced in India, and I got an invite for the trial. I stopped my gaming and those related activities recently, so I shared it with a friend of mine. He paired it with Xbox Game Pass and is having a good time with different games.&lt;/p&gt;\n&lt;p&gt;Got to meet some old friends that I didn&amp;rsquo;t meet in a long while; I felt human/grounded seeing the changes they have been through. I said this to one even: people are shaped by whom they interact with and whom they surround themselves with; they are sculpted subconsciously without them even realizing it&amp;hellip; seeing how differently they&amp;rsquo;ve changed.&lt;/p&gt;\n&lt;p&gt;The price of petrol has risen up so much&amp;hellip; right after the elections&amp;hellip; the politicians played their hand smartly&amp;hellip; in April 2026&amp;hellip; the price was around 99.82 rupees, and now it has been 106.xx rupees. Because of the ongoing conflict in the Middle East—specifically tensions surrounding the Strait of Hormuz disrupting global oil supply chains, the prices are expected to go even higher? I was planning to buy a good electric scooty, but then my friend&amp;rsquo;s dad said things to convince me not to take it (it was just before the war)&amp;hellip; now I&amp;rsquo;m feeling maybe I shouldn&amp;rsquo;t have brought it up with him, and as a result, wouldn&amp;rsquo;t have been influenced by him. I have to search for one electric scooty that&amp;rsquo;s good for the short term and also for the long term, considering the serviceability and all.&lt;/p&gt;\n&lt;p&gt;On top of that, I have to change the apartment in Chennai. I remember writing in January 2 years ago when I moved into this apartment. I&amp;rsquo;m thinking of moving back to my hometown and staying over at a friend&amp;rsquo;s place shortly&amp;hellip; until I&amp;rsquo;m done with a few things? Idk&amp;hellip; or move to a new apartment in Chennai so I can still stay there and commute to the office&amp;hellip; Idk, I&amp;rsquo;m conflicted. Maybe I should stay in a hotel for the days I&amp;rsquo;m going to the office. Argh! These are just simple decisions but take away valuable time and energy.&lt;/p&gt;\n&lt;p&gt;And lastly, it&amp;rsquo;s been one year, huh! If you know what I mean, I have to update this section later&amp;hellip; about what this is&amp;hellip; at a later point in time.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve started a long stagnated item; I made progress one way or another. Future me, how did it go through? Were you able to handle it? If you wanted to talk to someone about anything, I&amp;rsquo;m here with you; look back&amp;hellip; read these items and reminisce (atow - raining outside, thunderstorm, parents sleeping, open window, sofa near window, wind on the back of the head, midnight).&lt;/p&gt;</description></item><item><title>Offline AI models on Phones &amp; PC - April 2026</title><link>//bluepie.in/2026/04/april-2026/</link><pubDate>Sun, 12 Apr 2026 05:49:01 +0000</pubDate><guid>//bluepie.in/2026/04/april-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Today I want to talk about, &lt;a href=&#34;https://ai.google.dev/gemma/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Gemma 4&lt;/a&gt;; this sets a new standard for open source AI models. I am astonished by the quality of this, though it has a small model size compared to the other big ones out there.. like &lt;a href=&#34;https://kimi.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Kimi K2.5 from Moonshot AI&lt;/a&gt;, that thing is a 1 trillion parameter model.. 1 trillion! and Gemma 4&amp;rsquo;s biggest is only 31 billion.. but here is the thing, Kimi K2.5 only uses 32 billion of those parameters at any given time, so they are not that far apart in practice.. and Gemma 4 punches way above its weight for how small it actually is..&lt;/p&gt;\n&lt;h2 id=&#34;gemma4&#34;&gt;gemma4&lt;/h2&gt;\n&lt;p&gt;With the Gemma models, the naming conventions highlight their architecture, specifically using &lt;code&gt;A&lt;/code&gt; for &amp;ldquo;active&amp;rdquo; parameters and &lt;code&gt;E&lt;/code&gt; for &amp;ldquo;edge&amp;rdquo; or &amp;ldquo;efficient&amp;rdquo; models. For instance, &lt;code&gt;E2B&lt;/code&gt; and &lt;code&gt;E4B&lt;/code&gt; designate lighter models optimized for edge devices. They also utilize a Mixture of Experts (MoE) architecture, like the 26B MoE. In an MoE model, only a subset of the total parameters are &amp;ldquo;active&amp;rdquo; (&lt;code&gt;A&lt;/code&gt;) for any given token. Think of it like a restaurant with 26 chefs, but only 4 are actually cooking your order at any point while the rest stand by with their own specializations. This allows it to run as fast as a 4B model while retaining the extensive knowledge of a 26B one. On the other hand, there is the 31B Dense model. That one just loads everything—all 31 billion parameters fire for every single token. No routing, no tricks, just brute force. It uses much more memory, but its performance is more predictable.&lt;/p&gt;\n&lt;p&gt;There are two classifications of models, and in each of these classifications there are multiple models that are released. Two of the four.. are for desktop/laptop, and the others, they created for edge devices. What they mean by edge devices are mobile phones, Raspberry Pi, and other less powerful devices or mobile.&lt;/p&gt;\n&lt;h2 id=&#34;on-pc&#34;&gt;On PC&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been a long time user of &lt;a href=&#34;https://lmstudio.ai/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;lm studio&lt;/a&gt;, it offers great features.. I like it because it offers ease of use, ability to manage different models all at once,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/lm-studio-ui.webp&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;from the official documentation. I can run up to 14 GB of graphics memory worth of model. The 31B dense model needs around 20+ GB at full precision.. so that is out for me. The 26B MoE is interesting though.. even though it has 26B total params you only compute 4B at a time, so it is lighter on the processor, but you still have to load all 26B into VRAM which I don&amp;rsquo;t have.. at least until now. The trick people use is Q4 quantization, which basically compresses the model down significantly. The 31B at Q4 can fit around 16-17 GB.. still a bit above what I got but getting closer. Maybe next GPU.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-laptop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;on-mobile&#34;&gt;On mobile&lt;/h2&gt;\n&lt;p&gt;It runs surprisingly fast and remains fairly accurate. The output isn&amp;rsquo;t exactly the same as the desktop models, largely because of how mobile inference is optimized. Instead of performing full, heavy computations on every layer from scratch for every word, mobile execution uses techniques like quantization and KV caching. Essentially, the model reuses pre-calculated states (like a memory map) on each layer for the context it has already processed, which drastically speeds up generation on phones. Because of these aggressive optimizations, it&amp;rsquo;s hard to measure its accuracy strictly against the uncompressed desktop versions. Still, to give it credit, it handles an impressive 140 languages easily; heck, I even used my native language and it worked great when I tried it.&lt;/p&gt;\n&lt;p&gt;To run these models locally, I am using &lt;strong&gt;&lt;a href=&#34;https://github.com/google-ai-edge/gallery&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google AI Edge Gallery&lt;/a&gt;&lt;/strong&gt;. It is an open-source, experimental mobile application from Google that showcases running Large Language Models entirely on-device (Android and iOS). Because the app uses &lt;a href=&#34;https://ai.google.dev/edge/litert&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiteRT&lt;/a&gt; (formerly TensorFlow Lite) and the &lt;a href=&#34;https://ai.google.dev/edge/mediapipe/solutions/genai/llm_inference&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LLM Inference API&lt;/a&gt;, the models execute offline directly on the phone&amp;rsquo;s hardware. This means complete data privacy with no cloud API costs. The app provides great features out of the box like LLM Chat, multimodal interactions to ask questions about images, a Prompt Lab for testing ideas locally.. and much more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-mobile.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal&#34;&gt;personal&lt;/h2&gt;\n&lt;p&gt;Hey, how are you doing? I hope everything is going well. I lost my iPhone this month; it was hectic. I lost it when traveling back from the office to my Chennai home. Although I have had the phone for a while now, and it&amp;rsquo;s old.. it still stings. My first time losing a phone. Why would I lose an iPhone? It was a series of events that happened that day. A few days before.. I accidentally bent the new glasses I got. Then during that day, the glasses got so loose that I couldn&amp;rsquo;t wear them the entire day. The temples (new term.. weird to call that piece that.. anyway) were so loose, they would have come into two pieces if I had worn them that day. So before that, a day ago, my mom got into a little accident. All of these in mind, I got into a weird headspace and it led to me losing the phone. I felt something drop.. but when I looked for it, it was still there where I kept the phone. On top of it, the pouch that I kept the phone in was torn a bit and I hadn&amp;rsquo;t changed it.. which also contributed to this. I&amp;rsquo;m never keeping a phone in a pouch again! ..that&amp;rsquo;s what I told myself, but future me will catch myself doing the same in the future, I think. After that.. I lost my building entrance card. My office card I had, but I lost the Olympia Guindy building card, which is another pain.. but at the time of updating this, on April 15, it was found and I went and collected it.&lt;/p&gt;\n&lt;p&gt;It&amp;rsquo;s been a long time since I bought a phone. Before, I used to develop for Android and iOS; now I don&amp;rsquo;t anymore. I don&amp;rsquo;t think I need an iPhone again.. a better deal is to buy a latest &lt;a href=&#34;https://www.qualcomm.com/snapdragon&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Snapdragon chipset&lt;/a&gt; phone and call it a day. Going to switch back to bigger XL screen phones.&lt;/p&gt;\n&lt;p&gt;Lots of things to do, yet you choose to have little moments of fun? At the moment, my brain can&amp;rsquo;t remember what pending things are left.&lt;/p&gt;\n&lt;p&gt;A friend stayed over for a while at the start of April. I had to work on weekends because of horrible management in the office. I try to do a small piece of work every day though, but still.. yeah. He was going on a trip.. north and back and stayed for a while. The initial plan was that he would stay here for a while, but then we would both go to our hometown. He is a childhood friend of mine, but work got hectic; I couldn&amp;rsquo;t make it as discussed next week and he had to go alone.&lt;/p&gt;\n&lt;p&gt;Last year at this time, I was playing &lt;a href=&#34;https://www.playstation.com/games/ghost-of-tsushima/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Ghost of Tsushima&lt;/a&gt; in my off-time. This year, I&amp;rsquo;m playing &lt;a href=&#34;https://megacrit.com/slaythespire2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Slay the Spire 2&lt;/a&gt; and &lt;a href=&#34;https://crimsondesert.pearlabyss.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Crimson Desert&lt;/a&gt; (released on March 19th, 2026) in my off-time.. dang nab it, I should give up this gaming.. I&amp;rsquo;m getting old.. but what if that changes me entirely? Risky..&lt;/p&gt;</description></item><item><title>&#39;AI Skills&#39;</title><link>//bluepie.in/2026/03/mar-2026/</link><pubDate>Tue, 03 Mar 2026 05:24:25 +0000</pubDate><guid>//bluepie.in/2026/03/mar-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Anthropic came and published their skills documentation on what they are roughly 2 months ago, but I suspect they and other AI companies were using them internally even before they published. The idea of people seeing them as a new thing and being astonished is abysmal..because we were already using it..but without the naming convention and the optimised context limiting. The concept is simple: you create a folder named skill you want, then inside it, the main file is called skill.md. This takes less context when ai reads it, as it just reads the frontmatter alone; you write detailed instructions below the frontmatter with different scripts and whatever that is necessary to finish that task/skill, you also writter a condition upon which the ai will trigger. The AI then keeps all these details and uses it ad hoc. The problem it solves is that this uses way less context than mcp or others and only triggers when necessary.&lt;/p&gt;\n&lt;h2 id=&#34;example-plantuml-generator&#34;&gt;Example: PlantUML Generator&lt;/h2&gt;\n&lt;p&gt;So the idea is simple; as you can infer from above, let&amp;rsquo;s go with an example. I wanted to create an architecture diagram skill in office. ..in office we use he PlantUML to create sequence diagrams.&lt;/p&gt;\n&lt;p&gt;The problem is you have to ask the AI to generate it, and then generate the image and maintain the image somewhere. The problem with this is it takes time and its a manual process. How can we use skills to automate this? We can write instructions to ask the AI chat / agent to be like, if a user asks x, then do the process and update the diagram. We can add these instructions in the skill.md file, keep the name and descriptions to work with the architecture diagram.&lt;/p&gt;\n&lt;p&gt;You can write scripts and then use them to generate things; the AI will execute them based on the instructions. The next is dependencies, if any. Since PlantUML is an open-source project, so we can download the jar file and put that in the skills folder. since its a java project..it&amp;rsquo;s platform independent, so no need to worry about different binaries&lt;/p&gt;\n&lt;p&gt;So everything can be packaged into a single skill.&lt;/p&gt;\n&lt;p&gt;you can see the skills folder here: &lt;a href=&#34;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;embedding-code-in-images&#34;&gt;Embedding Code in Images&lt;/h2&gt;\n&lt;p&gt;On top of the skill, I was thinking about for a optimization. In college, I conducted a Capture the Flag; the event was like Google CTF and other CTF events from that time. I was inspired by them, Anyway. one of the competitions was to find the hidden details from the image. Inside an image, you can add a data to it, it can be any data. So I was thinking, why can&amp;rsquo;t we use the same thing here? So I asked ai to write a script to embed code into the images. So the PlantUML diagram is stored inside the images, the images are the output of the code. It&amp;rsquo;s like a chicken and egg problem once you delete the code file from the repo.&lt;/p&gt;\n&lt;p&gt;We want crud options on this; the update image, edit image, and create image are just a set of instructions that the AI can read and execute.. in my case it will be Python scripts, when user asks to edit, it will extract the code from the image and create a temporary file we can work on, and when we ask to store it, it saves the source code, again inside the image, deleting the code file.&lt;/p&gt;\n&lt;h2 id=&#34;other-skills&#34;&gt;Other Skills&lt;/h2&gt;\n&lt;p&gt;There are other skills that I use here, it keeps changing overtime.&lt;/p&gt;\n&lt;p&gt;The main one I wanted to share is the PlantUML generator; it has all the features that I need to be adding to demonstrate the features of the skill.&lt;/p&gt;\n&lt;h2 id=&#34;sub-agents-lifecycle-hooks&#34;&gt;Sub-agents &amp;amp; Lifecycle Hooks&lt;/h2&gt;\n&lt;p&gt;At the end of Feb 2026, the Claude team updated the skillset to have custom features, like sub-agents. What it is, is that the sub-agent is being called by the main agent when doing a certain task.. so it will fix the context problem. it&amp;rsquo;s one of the major updates to skills; it&amp;rsquo;s the major one that makes a significant difference, because it won&amp;rsquo;t clutter the main context; the main agent is not interfered with.&lt;/p&gt;\n&lt;p&gt;It also has access to the lifecycle events, Lifecycle hooks (on_start, on_success, on_error, PreToolUse, etc.).. but i haven&amp;rsquo;t adopted them yet; but this will help me with the cryo vault project: &lt;a href=&#34;https://github.com/aghontpi/cryo-vault&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/cryo-vault&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m having to juggle a lot of things in my life currently; finding focus time to think on what is essential or next is hard with this attention-seeking world; socializing; maintaining health and ad hoc new plans; argh! I started to have a new habit; hopefully, when I am consistent on this.. I&amp;rsquo;ll post on this later!.. future me..! what is happening? hope you are good!&lt;/p&gt;</description></item><item><title>Selfhosting an Automation tool - N8N</title><link>//bluepie.in/2026/02/feb-2026-2/</link><pubDate>Sat, 28 Feb 2026 15:17:41 +0000</pubDate><guid>//bluepie.in/2026/02/feb-2026-2/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;In the previous post, I was talking about &lt;a href=&#34;https://claude.com/skills&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;claude&lt;/a&gt; skills. This is about automation, no code/ minimal solutions. At this point, it&amp;rsquo;s been over 1.5 to 2 years since &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and automation; nowadays it&amp;rsquo;s easy to just build an app/ ask AI to build the app instead of creating the automation, it&amp;rsquo;s easier that way, but regardless, let&amp;rsquo;s see how the future goes; I&amp;rsquo;m already seeing the trend of creating custom apps for each one&amp;rsquo;s personal preference.&lt;/p&gt;\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;Instead of creating projects for each use and hosting it for every use case, I thought of using automation tools and solving those problems; for ex, one such problem is creating a diet maintainer; I ranted in a previous post that I gained a couple of kgs in a short while, but I&amp;rsquo;m trying to reduce it; so I have to keep track of the amount of calories, etc., but I want this to be an Excel sheet for tracking, yes there are apps for that; but I wanted a simple chat interface where I can type the food I ate and when I ate it, and it should approximate the calories, and when I ask how much is left, it should give back the amount of food that I can eat in the remaining part of the day/ from that point onwards.&lt;/p&gt;\n&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;\n&lt;p&gt;I choose n8n. The main reason I prefer it is the immense community support it has, it&amp;rsquo;s incredibly popular, which means if I run into a wall, someone else has probably already solved it and posted about it. Plus, being able to tinker with it under the hood while self-hosting is a massive bonus compared to locked-in cloud alternatives.&lt;/p&gt;\n&lt;p&gt;Speaking of alternatives, there are quite a few out there:\n- &lt;a href=&#34;https://www.make.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Make (formerly Integromat)&lt;/a&gt;: Great visual builder,\n- &lt;a href=&#34;https://zapier.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Zapier&lt;/a&gt;: The biggest name out there with the most integrations,\n- &lt;a href=&#34;https://pipedream.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Pipedream&lt;/a&gt;: Fantastic for developers who want to write code snippets, but maybe overkill if you just want pure drag-and-drop.\n- &lt;a href=&#34;https://www.activepieces.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Activepieces&lt;/a&gt;: is similar to n8n&lt;/p&gt;\n&lt;h2 id=&#34;self-hosting&#34;&gt;Self hosting&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Here is the official n8n documentation guide&lt;/a&gt;, I started to see the official docs of n8n. One thing I got surprised by is that they have incorporated AI, and that AI is surprisingly good at providing the right instructions, because when you host with different servers, server config differs for each one like &lt;a href=&#34;https://caddyserver.com/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;caddy&lt;/a&gt; / &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;/ etc., and for automation to work, it has to be able to receive webhooks, and alongside things like if it&amp;rsquo;s behind a proxy; it must be configured to tell that you are behind a proxy, and based on that it can handle the incoming request, etc.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll save you the trouble and give the config here; basically I was able to set up the Nginx server pretty easily using the AI bot that they have incorporated in their site. Below is all the config I needed to be able to run this from &lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; and &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;:&lt;/p&gt;\n&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I like to run things with &lt;a href=&#34;https://docs.podman.io/en/latest/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;podman&lt;/a&gt;/&lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; as it removes the dependency from my cloud machine.&lt;/p&gt;\n&lt;p&gt;Obviously I have to configure my domain name to &lt;a href=&#34;https://www.godaddy.com/help&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;godaddy&lt;/a&gt;/&lt;a href=&#34;https://developers.cloudflare.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt; and deploy SSL certificates; I&amp;rsquo;ve covered that plenty in my other posts:&lt;/p&gt;\n&lt;p&gt;docker config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;podman run -d --restart unless-stopped \\\n--name n8n \\\n-p 5678:5678 \\\n-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \\\n-e N8N_RUNNERS_ENABLED=true \\\n-e WEBHOOK_URL=https://&amp;lt;server-name&amp;gt;/ \\\n-e N8N_PROXY_HOPS=1 \\\n-e N8N_HOST=&amp;lt;server-name&amp;gt;\n-e N8N_PORT=5678 \\\n-e N8N_PROTOCOL=https \\\n-v /mnt/podman-data/n8n_data:/home/node/.n8n:Z \\\ndocker.n8n.io/n8nio/n8n\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;nginx config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;server {\nlisten 80;\nserver_name &amp;lt;server-name&amp;gt;\nlocation / {\nproxy_pass http://127.0.0.1:5678;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_set_header X-Forwarded-Port $server_port;\nchunked_transfer_encoding off;\nproxy_buffering off;\nproxy_cache off;\n}\nlocation /webhook/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\nlocation /webhook-test/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-how-to-configure&#34;&gt;Configuring - How to configure&lt;/h2&gt;\n&lt;p&gt;Modifying a telegram bot to track the calory intake of the day based on estimates from me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/overview.png&#34; alt=&#34;Overview&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is initial learning time to get used to what each option is, but when you get the hang of it, it&amp;rsquo;s pretty easy, since I&amp;rsquo;ve done drag and drop editors before; I caught up easily in approx 5-10ish mins.&lt;/p&gt;\n&lt;p&gt;Create a &lt;a href=&#34;https://core.telegram.org/bots/api&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;telegram&lt;/a&gt; node and select the appropriate Telegram account credential. Set the &lt;strong&gt;Trigger On&lt;/strong&gt; parameter to &lt;code&gt;Message&lt;/code&gt; so it triggers on new incoming messages of any kind (text, photo, sticker, etc.). Note that due to Telegram API limitations, you can only use one Telegram trigger per bot. Also ensure there is validation to only process messages from your known user ID, otherwise the bot will refuse to respond.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/telegram-trigger-receive.png&#34; alt=&#34;Telegram Trigger Settings&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Add an &lt;strong&gt;Analyze Text Message&lt;/strong&gt; node and connect it to your Google Gemini (or equivalent API) configuration. Set the Resource to &lt;code&gt;Text&lt;/code&gt; and Operation to &lt;code&gt;Message a Model&lt;/code&gt;, using the &lt;code&gt;models/gemini-2.5-flash&lt;/code&gt; model so its fast and reliable. models cheaper than 2.5-flash are not reliable based on testing. Within the node, establish a system prompt (Role: &lt;code&gt;Model&lt;/code&gt;) instructing the AI to process user input into structured nutritional data—identifying meal components, estimating portion weights, and warning it not to reveal its internal steps. Pass the user input as a prompt (Role: &lt;code&gt;User&lt;/code&gt;) using the expression &lt;code&gt;{{ $json.message }}&lt;/code&gt; along with today&amp;rsquo;s date context: &lt;code&gt;{{ $today.format(&#39;yyyy-MM-dd&#39;) }}&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/using-gemini-ai-via-api.png&#34; alt=&#34;AI Processing Configuration&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;To store the parsed responses, add a Google Sheets node configured to &amp;ldquo;Append&amp;rdquo; rows, and set it to &lt;strong&gt;Map Each Column Manually&lt;/strong&gt;. You can map fields originating from the trigger and model response:\n- &lt;strong&gt;User_ID&lt;/strong&gt;: &lt;code&gt;{{ $json.chat_id }}&lt;/code&gt;\n- &lt;strong&gt;Date&lt;/strong&gt;: Extract and format the date directly with &lt;code&gt;{{ DateTime.fromSeconds($json.date).format(&#39;yyyy-LL-d&#39;) }}&lt;/code&gt;.\n- Dietary metrics like &lt;strong&gt;Meal_description&lt;/strong&gt;, &lt;strong&gt;Calories&lt;/strong&gt;, &lt;strong&gt;Proteins&lt;/strong&gt;, &lt;strong&gt;Carbs&lt;/strong&gt;, and &lt;strong&gt;Fats&lt;/strong&gt; can be defined automatically by mapping them from structured outputs generated by the AI model.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/append-to-excel-sheet.png&#34; alt=&#34;Append to Google Sheets&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is all sorts of existing nodes (in &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;, a &amp;ldquo;node&amp;rdquo; is basically a building block or a step in your automation workflow—it can be a trigger that starts the flow, an action that connects to a specific app&amp;rsquo;s API like sending a message, or a logic step that filters or processes data) that we can use. For this use case, reusing the &lt;a href=&#34;https://developers.google.com/sheets/api/guides/concepts&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google sheet&lt;/a&gt; node from official requires getting API keys, but that is well documented by &lt;a href=&#34;https://docs.n8n.io/integrations/builtin/credentials/google/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and &lt;a href=&#34;https://developers.google.com/workspace/guides/create-credentials&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;Finally, configure another Telegram node to send a reply back to the user. Set the Resource to &lt;code&gt;Message&lt;/code&gt; and Operation to &lt;code&gt;Send Message&lt;/code&gt;. For the &lt;strong&gt;Chat ID&lt;/strong&gt;, retrieve it from the initial trigger using &lt;code&gt;{{ $(&#39;Telegram Trigger&#39;).item.json.message.chat.id }}&lt;/code&gt;. Include the formatted Text from the AI calculation &lt;code&gt;{{ $json.message }}&lt;/code&gt;. Under Additional Fields, be sure to set &lt;strong&gt;Parse Mode&lt;/strong&gt; to &lt;code&gt;MarkdownV2&lt;/code&gt; so any formatting from the AI looks correct, and toggle off &amp;ldquo;Append n8n Attribution&amp;rdquo; for a cleaner message.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/send-telegram-msg-back.png&#34; alt=&#34;Send Telegram Summary Message&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For the initial setup, because it&amp;rsquo;s new, I was experimenting and took 4-5 hours, because it was interesting; but been using that for maintaining calories now; it&amp;rsquo;s great.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;AI is more prevalent these days, instead of building these automation no code solutions via these tools, users directly build with AI, there are pros and cons.. it&amp;rsquo;s already deployed on the server (the automation tool, one time setup), can deploy any number of times.. limit depends on the server limits, I&amp;rsquo;m okay.. but people need to know of how to debug in case of error, I mean there is AI for that too; if using &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;/automation.. everything is contained within this little confinement work area.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll be trying out other automation experiments, this is serving me well, recently I gained a lot of weight, though not overweight.. I still look fit from an outside view; but a little fat in stomach and back.. I started eating snacks every night and lots of oats (used to prepare the previous night with milk and sugar.. keep it in freezer).. the next morning eating it, I drove this diet for some time, it was good for my morning routine (not going to office on an empty stomach), but the problem was I gained like 5 kgs, I have never been this much weight in my entire life! but I&amp;rsquo;m 6ft.. so the weight is kinda explainable.&lt;/p&gt;\n&lt;p&gt;Me when 76kgs, ideal for my height, less fat content ratio in my body; this is before I got muscle buildup.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/me-when-76.jpg&#34; alt=&#34;image when I was 76kgs&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;but in terms of physique, , I got visibly more muscle development..after above image, idk.. started to go to gym again, let&amp;rsquo;s see, till now I&amp;rsquo;ve been continuing to do it every day except Sunday at home. But I&amp;rsquo;ll track the progress.&lt;/p&gt;\n&lt;p&gt;Updating this part as of March 2026, it&amp;rsquo;s happening again, I feel out of place, everyone around here is behind.. so behind .. than I expect them to be in, when I see, I see a fine line between things and it&amp;rsquo;s like I can slip in between them.. and go past beyond. I see it like this, a highway road that is busy with vehicles going left and right, but I see a path in slow motion that I can slip in between and move to the other side, I see what others are not seeing, IDK how long I will be blessed with this gift or curse that I have, can&amp;rsquo;t share this with anyone, I exposed this lil bit to sq#3, ~Future me is this happening again? Hope you landed on things, learned from past.&lt;/p&gt;</description></item><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item></channel></rss>"}, "748a7d747986620c3df0c4faa5374d0aa8dc0747": {"t": 1785770732.5495186, "n": 155239, "h": "433adba3c66ecc37", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Fri, 01 May 2026 17:54:17 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Comparing Free Chinese Models For Daily Use - May 2026</title><link>//bluepie.in/2026/05/may-2026/</link><pubDate>Fri, 01 May 2026 17:54:17 +0000</pubDate><guid>//bluepie.in/2026/05/may-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I wanted to test the performance and accuracy of the ai models at the time of writing this (May 2026, so I can leverage them in my day-to-day tasks).\nCurrently, I am testing 3 models: 1. Deepseek (v4 just got released) 2. GLM 3. Qwen.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-a-common-task&#34;&gt;Identifying a common task&lt;/h2&gt;\n&lt;p&gt;At the time of writing this, there are Tamil Nadu elections. I&amp;rsquo;ve made up my mind to ask the AI to visualize the TN elections and would rate them based on their accuracy and how beautifully they present the data.&lt;/p&gt;\n&lt;p&gt;Price-wise, Deepseek and the Chinese models are dominating the leaderboards for the price-to-perfomance ratio.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/may-2026/leaderboard-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/may-2026/leaderboard-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;deepseek&#34;&gt;Deepseek&lt;/h2&gt;\n&lt;p&gt;Though the design is okey, the accuracy is great.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/deepseek.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;glm&#34;&gt;GLM&lt;/h2&gt;\n&lt;p&gt;The design is great, especially where each seat is represented as an arc and color-coded, but the data is completely wrong.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/glm.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;qwen&#34;&gt;Qwen&lt;/h2&gt;\n&lt;p&gt;The design is mid, and the accuracy is also wrong.&lt;/p&gt;\n&lt;p&gt;output:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/qwen.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion-winner&#34;&gt;Conclusion / Winner&lt;/h2&gt;\n&lt;p&gt;From the different conclusions, we have different results. Only the first model&amp;hellip; both the design and the model accuracy are good. Note that we have selected the expert mode.&lt;/p&gt;\n&lt;p&gt;In GLM&amp;hellip; the design is great without even the correct prompt&amp;hellip; but the accuracy is bad.&lt;/p&gt;\n&lt;p&gt;With this short test, I was able to figure out which AI I can rely on the most.&lt;/p&gt;\n&lt;p&gt;Results of AI:\n&lt;img src=&#34;//bluepie.in/images/posts/may-2026/results-of-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Updated as of May 24th. Got to stay home more even though I had a prod release from the office (working at VZ at this point in time), but food poisoning and dehydration happened to my dad&amp;hellip; had to take him to hospital emergency care, but he recovered quickly. The marriage that is planned in May is in limbo; the partner wants some things to be finished from her side, and I&amp;rsquo;m feeling kinda the same on some matters. My SO is trying to find a good company to work for, in between managing family and me, hard for her as well. Take small steps forward; though it doesn&amp;rsquo;t make any sense at the current moment, in the long run, it will make sense.&lt;/p&gt;\n&lt;p&gt;Since COVID, I took medical insurance for my parents, both of them&amp;hellip; I didn&amp;rsquo;t think it would be of much use when my dad got admitted to the hospital&amp;hellip; but the hospital successfully processed it. In the end, I got the money I paid for the entire hospital visit, though others said they were useless (others meaning some of my brothers and others)&amp;hellip; I think it&amp;rsquo;s useful and provides some mental relief when something does happen in case of emergencies. I have insurance with the office that covers me but also my parents, but I&amp;rsquo;ve kept this separately. Thinking of taking separate health insurance and life insurance; don&amp;rsquo;t know what I&amp;rsquo;ve decided yet. I&amp;rsquo;ll let you know in the future what I decide.&lt;/p&gt;\n&lt;p&gt;Unrelated to this, GeForce Now got announced in India, and I got an invite for the trial. I stopped my gaming and those related activities recently, so I shared it with a friend of mine. He paired it with Xbox Game Pass and is having a good time with different games.&lt;/p&gt;\n&lt;p&gt;Got to meet some old friends that I didn&amp;rsquo;t meet in a long while; I felt human/grounded seeing the changes they have been through. I said this to one even: people are shaped by whom they interact with and whom they surround themselves with; they are sculpted subconsciously without them even realizing it&amp;hellip; seeing how differently they&amp;rsquo;ve changed.&lt;/p&gt;\n&lt;p&gt;The price of petrol has risen up so much&amp;hellip; right after the elections&amp;hellip; the politicians played their hand smartly&amp;hellip; in April 2026&amp;hellip; the price was around 99.82 rupees, and now it has been 106.xx rupees. Because of the ongoing conflict in the Middle East—specifically tensions surrounding the Strait of Hormuz disrupting global oil supply chains, the prices are expected to go even higher? I was planning to buy a good electric scooty, but then my friend&amp;rsquo;s dad said things to convince me not to take it (it was just before the war)&amp;hellip; now I&amp;rsquo;m feeling maybe I shouldn&amp;rsquo;t have brought it up with him, and as a result, wouldn&amp;rsquo;t have been influenced by him. I have to search for one electric scooty that&amp;rsquo;s good for the short term and also for the long term, considering the serviceability and all.&lt;/p&gt;\n&lt;p&gt;On top of that, I have to change the apartment in Chennai. I remember writing in January 2 years ago when I moved into this apartment. I&amp;rsquo;m thinking of moving back to my hometown and staying over at a friend&amp;rsquo;s place shortly&amp;hellip; until I&amp;rsquo;m done with a few things? Idk&amp;hellip; or move to a new apartment in Chennai so I can still stay there and commute to the office&amp;hellip; Idk, I&amp;rsquo;m conflicted. Maybe I should stay in a hotel for the days I&amp;rsquo;m going to the office. Argh! These are just simple decisions but take away valuable time and energy.&lt;/p&gt;\n&lt;p&gt;And lastly, it&amp;rsquo;s been one year, huh! If you know what I mean, I have to update this section later&amp;hellip; about what this is&amp;hellip; at a later point in time.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve started a long stagnated item; I made progress one way or another. Future me, how did it go through? Were you able to handle it? If you wanted to talk to someone about anything, I&amp;rsquo;m here with you; look back&amp;hellip; read these items and reminisce (atow - raining outside, thunderstorm, parents sleeping, open window, sofa near window, wind on the back of the head, midnight).&lt;/p&gt;</description></item><item><title>Offline AI models on Phones &amp; PC - April 2026</title><link>//bluepie.in/2026/04/april-2026/</link><pubDate>Sun, 12 Apr 2026 05:49:01 +0000</pubDate><guid>//bluepie.in/2026/04/april-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Today I want to talk about, &lt;a href=&#34;https://ai.google.dev/gemma/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Gemma 4&lt;/a&gt;; this sets a new standard for open source AI models. I am astonished by the quality of this, though it has a small model size compared to the other big ones out there.. like &lt;a href=&#34;https://kimi.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Kimi K2.5 from Moonshot AI&lt;/a&gt;, that thing is a 1 trillion parameter model.. 1 trillion! and Gemma 4&amp;rsquo;s biggest is only 31 billion.. but here is the thing, Kimi K2.5 only uses 32 billion of those parameters at any given time, so they are not that far apart in practice.. and Gemma 4 punches way above its weight for how small it actually is..&lt;/p&gt;\n&lt;h2 id=&#34;gemma4&#34;&gt;gemma4&lt;/h2&gt;\n&lt;p&gt;With the Gemma models, the naming conventions highlight their architecture, specifically using &lt;code&gt;A&lt;/code&gt; for &amp;ldquo;active&amp;rdquo; parameters and &lt;code&gt;E&lt;/code&gt; for &amp;ldquo;edge&amp;rdquo; or &amp;ldquo;efficient&amp;rdquo; models. For instance, &lt;code&gt;E2B&lt;/code&gt; and &lt;code&gt;E4B&lt;/code&gt; designate lighter models optimized for edge devices. They also utilize a Mixture of Experts (MoE) architecture, like the 26B MoE. In an MoE model, only a subset of the total parameters are &amp;ldquo;active&amp;rdquo; (&lt;code&gt;A&lt;/code&gt;) for any given token. Think of it like a restaurant with 26 chefs, but only 4 are actually cooking your order at any point while the rest stand by with their own specializations. This allows it to run as fast as a 4B model while retaining the extensive knowledge of a 26B one. On the other hand, there is the 31B Dense model. That one just loads everything—all 31 billion parameters fire for every single token. No routing, no tricks, just brute force. It uses much more memory, but its performance is more predictable.&lt;/p&gt;\n&lt;p&gt;There are two classifications of models, and in each of these classifications there are multiple models that are released. Two of the four.. are for desktop/laptop, and the others, they created for edge devices. What they mean by edge devices are mobile phones, Raspberry Pi, and other less powerful devices or mobile.&lt;/p&gt;\n&lt;h2 id=&#34;on-pc&#34;&gt;On PC&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been a long time user of &lt;a href=&#34;https://lmstudio.ai/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;lm studio&lt;/a&gt;, it offers great features.. I like it because it offers ease of use, ability to manage different models all at once,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/lm-studio-ui.webp&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;from the official documentation. I can run up to 14 GB of graphics memory worth of model. The 31B dense model needs around 20+ GB at full precision.. so that is out for me. The 26B MoE is interesting though.. even though it has 26B total params you only compute 4B at a time, so it is lighter on the processor, but you still have to load all 26B into VRAM which I don&amp;rsquo;t have.. at least until now. The trick people use is Q4 quantization, which basically compresses the model down significantly. The 31B at Q4 can fit around 16-17 GB.. still a bit above what I got but getting closer. Maybe next GPU.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-laptop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;on-mobile&#34;&gt;On mobile&lt;/h2&gt;\n&lt;p&gt;It runs surprisingly fast and remains fairly accurate. The output isn&amp;rsquo;t exactly the same as the desktop models, largely because of how mobile inference is optimized. Instead of performing full, heavy computations on every layer from scratch for every word, mobile execution uses techniques like quantization and KV caching. Essentially, the model reuses pre-calculated states (like a memory map) on each layer for the context it has already processed, which drastically speeds up generation on phones. Because of these aggressive optimizations, it&amp;rsquo;s hard to measure its accuracy strictly against the uncompressed desktop versions. Still, to give it credit, it handles an impressive 140 languages easily; heck, I even used my native language and it worked great when I tried it.&lt;/p&gt;\n&lt;p&gt;To run these models locally, I am using &lt;strong&gt;&lt;a href=&#34;https://github.com/google-ai-edge/gallery&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google AI Edge Gallery&lt;/a&gt;&lt;/strong&gt;. It is an open-source, experimental mobile application from Google that showcases running Large Language Models entirely on-device (Android and iOS). Because the app uses &lt;a href=&#34;https://ai.google.dev/edge/litert&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiteRT&lt;/a&gt; (formerly TensorFlow Lite) and the &lt;a href=&#34;https://ai.google.dev/edge/mediapipe/solutions/genai/llm_inference&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LLM Inference API&lt;/a&gt;, the models execute offline directly on the phone&amp;rsquo;s hardware. This means complete data privacy with no cloud API costs. The app provides great features out of the box like LLM Chat, multimodal interactions to ask questions about images, a Prompt Lab for testing ideas locally.. and much more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-mobile.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal&#34;&gt;personal&lt;/h2&gt;\n&lt;p&gt;Hey, how are you doing? I hope everything is going well. I lost my iPhone this month; it was hectic. I lost it when traveling back from the office to my Chennai home. Although I have had the phone for a while now, and it&amp;rsquo;s old.. it still stings. My first time losing a phone. Why would I lose an iPhone? It was a series of events that happened that day. A few days before.. I accidentally bent the new glasses I got. Then during that day, the glasses got so loose that I couldn&amp;rsquo;t wear them the entire day. The temples (new term.. weird to call that piece that.. anyway) were so loose, they would have come into two pieces if I had worn them that day. So before that, a day ago, my mom got into a little accident. All of these in mind, I got into a weird headspace and it led to me losing the phone. I felt something drop.. but when I looked for it, it was still there where I kept the phone. On top of it, the pouch that I kept the phone in was torn a bit and I hadn&amp;rsquo;t changed it.. which also contributed to this. I&amp;rsquo;m never keeping a phone in a pouch again! ..that&amp;rsquo;s what I told myself, but future me will catch myself doing the same in the future, I think. After that.. I lost my building entrance card. My office card I had, but I lost the Olympia Guindy building card, which is another pain.. but at the time of updating this, on April 15, it was found and I went and collected it.&lt;/p&gt;\n&lt;p&gt;It&amp;rsquo;s been a long time since I bought a phone. Before, I used to develop for Android and iOS; now I don&amp;rsquo;t anymore. I don&amp;rsquo;t think I need an iPhone again.. a better deal is to buy a latest &lt;a href=&#34;https://www.qualcomm.com/snapdragon&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Snapdragon chipset&lt;/a&gt; phone and call it a day. Going to switch back to bigger XL screen phones.&lt;/p&gt;\n&lt;p&gt;Lots of things to do, yet you choose to have little moments of fun? At the moment, my brain can&amp;rsquo;t remember what pending things are left.&lt;/p&gt;\n&lt;p&gt;A friend stayed over for a while at the start of April. I had to work on weekends because of horrible management in the office. I try to do a small piece of work every day though, but still.. yeah. He was going on a trip.. north and back and stayed for a while. The initial plan was that he would stay here for a while, but then we would both go to our hometown. He is a childhood friend of mine, but work got hectic; I couldn&amp;rsquo;t make it as discussed next week and he had to go alone.&lt;/p&gt;\n&lt;p&gt;Last year at this time, I was playing &lt;a href=&#34;https://www.playstation.com/games/ghost-of-tsushima/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Ghost of Tsushima&lt;/a&gt; in my off-time. This year, I&amp;rsquo;m playing &lt;a href=&#34;https://megacrit.com/slaythespire2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Slay the Spire 2&lt;/a&gt; and &lt;a href=&#34;https://crimsondesert.pearlabyss.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Crimson Desert&lt;/a&gt; (released on March 19th, 2026) in my off-time.. dang nab it, I should give up this gaming.. I&amp;rsquo;m getting old.. but what if that changes me entirely? Risky..&lt;/p&gt;</description></item><item><title>&#39;AI Skills&#39;</title><link>//bluepie.in/2026/03/mar-2026/</link><pubDate>Tue, 03 Mar 2026 05:24:25 +0000</pubDate><guid>//bluepie.in/2026/03/mar-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Anthropic came and published their skills documentation on what they are roughly 2 months ago, but I suspect they and other AI companies were using them internally even before they published. The idea of people seeing them as a new thing and being astonished is abysmal..because we were already using it..but without the naming convention and the optimised context limiting. The concept is simple: you create a folder named skill you want, then inside it, the main file is called skill.md. This takes less context when ai reads it, as it just reads the frontmatter alone; you write detailed instructions below the frontmatter with different scripts and whatever that is necessary to finish that task/skill, you also writter a condition upon which the ai will trigger. The AI then keeps all these details and uses it ad hoc. The problem it solves is that this uses way less context than mcp or others and only triggers when necessary.&lt;/p&gt;\n&lt;h2 id=&#34;example-plantuml-generator&#34;&gt;Example: PlantUML Generator&lt;/h2&gt;\n&lt;p&gt;So the idea is simple; as you can infer from above, let&amp;rsquo;s go with an example. I wanted to create an architecture diagram skill in office. ..in office we use he PlantUML to create sequence diagrams.&lt;/p&gt;\n&lt;p&gt;The problem is you have to ask the AI to generate it, and then generate the image and maintain the image somewhere. The problem with this is it takes time and its a manual process. How can we use skills to automate this? We can write instructions to ask the AI chat / agent to be like, if a user asks x, then do the process and update the diagram. We can add these instructions in the skill.md file, keep the name and descriptions to work with the architecture diagram.&lt;/p&gt;\n&lt;p&gt;You can write scripts and then use them to generate things; the AI will execute them based on the instructions. The next is dependencies, if any. Since PlantUML is an open-source project, so we can download the jar file and put that in the skills folder. since its a java project..it&amp;rsquo;s platform independent, so no need to worry about different binaries&lt;/p&gt;\n&lt;p&gt;So everything can be packaged into a single skill.&lt;/p&gt;\n&lt;p&gt;you can see the skills folder here: &lt;a href=&#34;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;embedding-code-in-images&#34;&gt;Embedding Code in Images&lt;/h2&gt;\n&lt;p&gt;On top of the skill, I was thinking about for a optimization. In college, I conducted a Capture the Flag; the event was like Google CTF and other CTF events from that time. I was inspired by them, Anyway. one of the competitions was to find the hidden details from the image. Inside an image, you can add a data to it, it can be any data. So I was thinking, why can&amp;rsquo;t we use the same thing here? So I asked ai to write a script to embed code into the images. So the PlantUML diagram is stored inside the images, the images are the output of the code. It&amp;rsquo;s like a chicken and egg problem once you delete the code file from the repo.&lt;/p&gt;\n&lt;p&gt;We want crud options on this; the update image, edit image, and create image are just a set of instructions that the AI can read and execute.. in my case it will be Python scripts, when user asks to edit, it will extract the code from the image and create a temporary file we can work on, and when we ask to store it, it saves the source code, again inside the image, deleting the code file.&lt;/p&gt;\n&lt;h2 id=&#34;other-skills&#34;&gt;Other Skills&lt;/h2&gt;\n&lt;p&gt;There are other skills that I use here, it keeps changing overtime.&lt;/p&gt;\n&lt;p&gt;The main one I wanted to share is the PlantUML generator; it has all the features that I need to be adding to demonstrate the features of the skill.&lt;/p&gt;\n&lt;h2 id=&#34;sub-agents-lifecycle-hooks&#34;&gt;Sub-agents &amp;amp; Lifecycle Hooks&lt;/h2&gt;\n&lt;p&gt;At the end of Feb 2026, the Claude team updated the skillset to have custom features, like sub-agents. What it is, is that the sub-agent is being called by the main agent when doing a certain task.. so it will fix the context problem. it&amp;rsquo;s one of the major updates to skills; it&amp;rsquo;s the major one that makes a significant difference, because it won&amp;rsquo;t clutter the main context; the main agent is not interfered with.&lt;/p&gt;\n&lt;p&gt;It also has access to the lifecycle events, Lifecycle hooks (on_start, on_success, on_error, PreToolUse, etc.).. but i haven&amp;rsquo;t adopted them yet; but this will help me with the cryo vault project: &lt;a href=&#34;https://github.com/aghontpi/cryo-vault&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/cryo-vault&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m having to juggle a lot of things in my life currently; finding focus time to think on what is essential or next is hard with this attention-seeking world; socializing; maintaining health and ad hoc new plans; argh! I started to have a new habit; hopefully, when I am consistent on this.. I&amp;rsquo;ll post on this later!.. future me..! what is happening? hope you are good!&lt;/p&gt;</description></item><item><title>Selfhosting an Automation tool - N8N</title><link>//bluepie.in/2026/02/feb-2026-2/</link><pubDate>Sat, 28 Feb 2026 15:17:41 +0000</pubDate><guid>//bluepie.in/2026/02/feb-2026-2/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;In the previous post, I was talking about &lt;a href=&#34;https://claude.com/skills&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;claude&lt;/a&gt; skills. This is about automation, no code/ minimal solutions. At this point, it&amp;rsquo;s been over 1.5 to 2 years since &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and automation; nowadays it&amp;rsquo;s easy to just build an app/ ask AI to build the app instead of creating the automation, it&amp;rsquo;s easier that way, but regardless, let&amp;rsquo;s see how the future goes; I&amp;rsquo;m already seeing the trend of creating custom apps for each one&amp;rsquo;s personal preference.&lt;/p&gt;\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;Instead of creating projects for each use and hosting it for every use case, I thought of using automation tools and solving those problems; for ex, one such problem is creating a diet maintainer; I ranted in a previous post that I gained a couple of kgs in a short while, but I&amp;rsquo;m trying to reduce it; so I have to keep track of the amount of calories, etc., but I want this to be an Excel sheet for tracking, yes there are apps for that; but I wanted a simple chat interface where I can type the food I ate and when I ate it, and it should approximate the calories, and when I ask how much is left, it should give back the amount of food that I can eat in the remaining part of the day/ from that point onwards.&lt;/p&gt;\n&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;\n&lt;p&gt;I choose n8n. The main reason I prefer it is the immense community support it has, it&amp;rsquo;s incredibly popular, which means if I run into a wall, someone else has probably already solved it and posted about it. Plus, being able to tinker with it under the hood while self-hosting is a massive bonus compared to locked-in cloud alternatives.&lt;/p&gt;\n&lt;p&gt;Speaking of alternatives, there are quite a few out there:\n- &lt;a href=&#34;https://www.make.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Make (formerly Integromat)&lt;/a&gt;: Great visual builder,\n- &lt;a href=&#34;https://zapier.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Zapier&lt;/a&gt;: The biggest name out there with the most integrations,\n- &lt;a href=&#34;https://pipedream.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Pipedream&lt;/a&gt;: Fantastic for developers who want to write code snippets, but maybe overkill if you just want pure drag-and-drop.\n- &lt;a href=&#34;https://www.activepieces.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Activepieces&lt;/a&gt;: is similar to n8n&lt;/p&gt;\n&lt;h2 id=&#34;self-hosting&#34;&gt;Self hosting&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Here is the official n8n documentation guide&lt;/a&gt;, I started to see the official docs of n8n. One thing I got surprised by is that they have incorporated AI, and that AI is surprisingly good at providing the right instructions, because when you host with different servers, server config differs for each one like &lt;a href=&#34;https://caddyserver.com/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;caddy&lt;/a&gt; / &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;/ etc., and for automation to work, it has to be able to receive webhooks, and alongside things like if it&amp;rsquo;s behind a proxy; it must be configured to tell that you are behind a proxy, and based on that it can handle the incoming request, etc.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll save you the trouble and give the config here; basically I was able to set up the Nginx server pretty easily using the AI bot that they have incorporated in their site. Below is all the config I needed to be able to run this from &lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; and &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;:&lt;/p&gt;\n&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I like to run things with &lt;a href=&#34;https://docs.podman.io/en/latest/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;podman&lt;/a&gt;/&lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; as it removes the dependency from my cloud machine.&lt;/p&gt;\n&lt;p&gt;Obviously I have to configure my domain name to &lt;a href=&#34;https://www.godaddy.com/help&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;godaddy&lt;/a&gt;/&lt;a href=&#34;https://developers.cloudflare.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt; and deploy SSL certificates; I&amp;rsquo;ve covered that plenty in my other posts:&lt;/p&gt;\n&lt;p&gt;docker config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;podman run -d --restart unless-stopped \\\n--name n8n \\\n-p 5678:5678 \\\n-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \\\n-e N8N_RUNNERS_ENABLED=true \\\n-e WEBHOOK_URL=https://&amp;lt;server-name&amp;gt;/ \\\n-e N8N_PROXY_HOPS=1 \\\n-e N8N_HOST=&amp;lt;server-name&amp;gt;\n-e N8N_PORT=5678 \\\n-e N8N_PROTOCOL=https \\\n-v /mnt/podman-data/n8n_data:/home/node/.n8n:Z \\\ndocker.n8n.io/n8nio/n8n\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;nginx config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;server {\nlisten 80;\nserver_name &amp;lt;server-name&amp;gt;\nlocation / {\nproxy_pass http://127.0.0.1:5678;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_set_header X-Forwarded-Port $server_port;\nchunked_transfer_encoding off;\nproxy_buffering off;\nproxy_cache off;\n}\nlocation /webhook/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\nlocation /webhook-test/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-how-to-configure&#34;&gt;Configuring - How to configure&lt;/h2&gt;\n&lt;p&gt;Modifying a telegram bot to track the calory intake of the day based on estimates from me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/overview.png&#34; alt=&#34;Overview&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is initial learning time to get used to what each option is, but when you get the hang of it, it&amp;rsquo;s pretty easy, since I&amp;rsquo;ve done drag and drop editors before; I caught up easily in approx 5-10ish mins.&lt;/p&gt;\n&lt;p&gt;Create a &lt;a href=&#34;https://core.telegram.org/bots/api&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;telegram&lt;/a&gt; node and select the appropriate Telegram account credential. Set the &lt;strong&gt;Trigger On&lt;/strong&gt; parameter to &lt;code&gt;Message&lt;/code&gt; so it triggers on new incoming messages of any kind (text, photo, sticker, etc.). Note that due to Telegram API limitations, you can only use one Telegram trigger per bot. Also ensure there is validation to only process messages from your known user ID, otherwise the bot will refuse to respond.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/telegram-trigger-receive.png&#34; alt=&#34;Telegram Trigger Settings&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Add an &lt;strong&gt;Analyze Text Message&lt;/strong&gt; node and connect it to your Google Gemini (or equivalent API) configuration. Set the Resource to &lt;code&gt;Text&lt;/code&gt; and Operation to &lt;code&gt;Message a Model&lt;/code&gt;, using the &lt;code&gt;models/gemini-2.5-flash&lt;/code&gt; model so its fast and reliable. models cheaper than 2.5-flash are not reliable based on testing. Within the node, establish a system prompt (Role: &lt;code&gt;Model&lt;/code&gt;) instructing the AI to process user input into structured nutritional data—identifying meal components, estimating portion weights, and warning it not to reveal its internal steps. Pass the user input as a prompt (Role: &lt;code&gt;User&lt;/code&gt;) using the expression &lt;code&gt;{{ $json.message }}&lt;/code&gt; along with today&amp;rsquo;s date context: &lt;code&gt;{{ $today.format(&#39;yyyy-MM-dd&#39;) }}&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/using-gemini-ai-via-api.png&#34; alt=&#34;AI Processing Configuration&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;To store the parsed responses, add a Google Sheets node configured to &amp;ldquo;Append&amp;rdquo; rows, and set it to &lt;strong&gt;Map Each Column Manually&lt;/strong&gt;. You can map fields originating from the trigger and model response:\n- &lt;strong&gt;User_ID&lt;/strong&gt;: &lt;code&gt;{{ $json.chat_id }}&lt;/code&gt;\n- &lt;strong&gt;Date&lt;/strong&gt;: Extract and format the date directly with &lt;code&gt;{{ DateTime.fromSeconds($json.date).format(&#39;yyyy-LL-d&#39;) }}&lt;/code&gt;.\n- Dietary metrics like &lt;strong&gt;Meal_description&lt;/strong&gt;, &lt;strong&gt;Calories&lt;/strong&gt;, &lt;strong&gt;Proteins&lt;/strong&gt;, &lt;strong&gt;Carbs&lt;/strong&gt;, and &lt;strong&gt;Fats&lt;/strong&gt; can be defined automatically by mapping them from structured outputs generated by the AI model.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/append-to-excel-sheet.png&#34; alt=&#34;Append to Google Sheets&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is all sorts of existing nodes (in &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;, a &amp;ldquo;node&amp;rdquo; is basically a building block or a step in your automation workflow—it can be a trigger that starts the flow, an action that connects to a specific app&amp;rsquo;s API like sending a message, or a logic step that filters or processes data) that we can use. For this use case, reusing the &lt;a href=&#34;https://developers.google.com/sheets/api/guides/concepts&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google sheet&lt;/a&gt; node from official requires getting API keys, but that is well documented by &lt;a href=&#34;https://docs.n8n.io/integrations/builtin/credentials/google/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and &lt;a href=&#34;https://developers.google.com/workspace/guides/create-credentials&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;Finally, configure another Telegram node to send a reply back to the user. Set the Resource to &lt;code&gt;Message&lt;/code&gt; and Operation to &lt;code&gt;Send Message&lt;/code&gt;. For the &lt;strong&gt;Chat ID&lt;/strong&gt;, retrieve it from the initial trigger using &lt;code&gt;{{ $(&#39;Telegram Trigger&#39;).item.json.message.chat.id }}&lt;/code&gt;. Include the formatted Text from the AI calculation &lt;code&gt;{{ $json.message }}&lt;/code&gt;. Under Additional Fields, be sure to set &lt;strong&gt;Parse Mode&lt;/strong&gt; to &lt;code&gt;MarkdownV2&lt;/code&gt; so any formatting from the AI looks correct, and toggle off &amp;ldquo;Append n8n Attribution&amp;rdquo; for a cleaner message.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/send-telegram-msg-back.png&#34; alt=&#34;Send Telegram Summary Message&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For the initial setup, because it&amp;rsquo;s new, I was experimenting and took 4-5 hours, because it was interesting; but been using that for maintaining calories now; it&amp;rsquo;s great.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;AI is more prevalent these days, instead of building these automation no code solutions via these tools, users directly build with AI, there are pros and cons.. it&amp;rsquo;s already deployed on the server (the automation tool, one time setup), can deploy any number of times.. limit depends on the server limits, I&amp;rsquo;m okay.. but people need to know of how to debug in case of error, I mean there is AI for that too; if using &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;/automation.. everything is contained within this little confinement work area.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll be trying out other automation experiments, this is serving me well, recently I gained a lot of weight, though not overweight.. I still look fit from an outside view; but a little fat in stomach and back.. I started eating snacks every night and lots of oats (used to prepare the previous night with milk and sugar.. keep it in freezer).. the next morning eating it, I drove this diet for some time, it was good for my morning routine (not going to office on an empty stomach), but the problem was I gained like 5 kgs, I have never been this much weight in my entire life! but I&amp;rsquo;m 6ft.. so the weight is kinda explainable.&lt;/p&gt;\n&lt;p&gt;Me when 76kgs, ideal for my height, less fat content ratio in my body; this is before I got muscle buildup.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/me-when-76.jpg&#34; alt=&#34;image when I was 76kgs&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;but in terms of physique, , I got visibly more muscle development..after above image, idk.. started to go to gym again, let&amp;rsquo;s see, till now I&amp;rsquo;ve been continuing to do it every day except Sunday at home. But I&amp;rsquo;ll track the progress.&lt;/p&gt;\n&lt;p&gt;Updating this part as of March 2026, it&amp;rsquo;s happening again, I feel out of place, everyone around here is behind.. so behind .. than I expect them to be in, when I see, I see a fine line between things and it&amp;rsquo;s like I can slip in between them.. and go past beyond. I see it like this, a highway road that is busy with vehicles going left and right, but I see a path in slow motion that I can slip in between and move to the other side, I see what others are not seeing, IDK how long I will be blessed with this gift or curse that I have, can&amp;rsquo;t share this with anyone, I exposed this lil bit to sq#3, ~Future me is this happening again? Hope you landed on things, learned from past.&lt;/p&gt;</description></item><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item></channel></rss>"}, "e962ccd3da96fd403f34c4949a0f0081cac3ea4a": {"t": 1785770734.723009, "n": 161878, "h": "d197e76045a977ab", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sun, 12 Apr 2026 05:49:01 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Offline AI models on Phones &amp; PC - April 2026</title><link>//bluepie.in/2026/04/april-2026/</link><pubDate>Sun, 12 Apr 2026 05:49:01 +0000</pubDate><guid>//bluepie.in/2026/04/april-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Today I want to talk about, &lt;a href=&#34;https://ai.google.dev/gemma/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Gemma 4&lt;/a&gt;; this sets a new standard for open source AI models. I am astonished by the quality of this, though it has a small model size compared to the other big ones out there.. like &lt;a href=&#34;https://kimi.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Kimi K2.5 from Moonshot AI&lt;/a&gt;, that thing is a 1 trillion parameter model.. 1 trillion! and Gemma 4&amp;rsquo;s biggest is only 31 billion.. but here is the thing, Kimi K2.5 only uses 32 billion of those parameters at any given time, so they are not that far apart in practice.. and Gemma 4 punches way above its weight for how small it actually is..&lt;/p&gt;\n&lt;h2 id=&#34;gemma4&#34;&gt;gemma4&lt;/h2&gt;\n&lt;p&gt;With the Gemma models, the naming conventions highlight their architecture, specifically using &lt;code&gt;A&lt;/code&gt; for &amp;ldquo;active&amp;rdquo; parameters and &lt;code&gt;E&lt;/code&gt; for &amp;ldquo;edge&amp;rdquo; or &amp;ldquo;efficient&amp;rdquo; models. For instance, &lt;code&gt;E2B&lt;/code&gt; and &lt;code&gt;E4B&lt;/code&gt; designate lighter models optimized for edge devices. They also utilize a Mixture of Experts (MoE) architecture, like the 26B MoE. In an MoE model, only a subset of the total parameters are &amp;ldquo;active&amp;rdquo; (&lt;code&gt;A&lt;/code&gt;) for any given token. Think of it like a restaurant with 26 chefs, but only 4 are actually cooking your order at any point while the rest stand by with their own specializations. This allows it to run as fast as a 4B model while retaining the extensive knowledge of a 26B one. On the other hand, there is the 31B Dense model. That one just loads everything—all 31 billion parameters fire for every single token. No routing, no tricks, just brute force. It uses much more memory, but its performance is more predictable.&lt;/p&gt;\n&lt;p&gt;There are two classifications of models, and in each of these classifications there are multiple models that are released. Two of the four.. are for desktop/laptop, and the others, they created for edge devices. What they mean by edge devices are mobile phones, Raspberry Pi, and other less powerful devices or mobile.&lt;/p&gt;\n&lt;h2 id=&#34;on-pc&#34;&gt;On PC&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been a long time user of &lt;a href=&#34;https://lmstudio.ai/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;lm studio&lt;/a&gt;, it offers great features.. I like it because it offers ease of use, ability to manage different models all at once,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/lm-studio-ui.webp&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;from the official documentation. I can run up to 14 GB of graphics memory worth of model. The 31B dense model needs around 20+ GB at full precision.. so that is out for me. The 26B MoE is interesting though.. even though it has 26B total params you only compute 4B at a time, so it is lighter on the processor, but you still have to load all 26B into VRAM which I don&amp;rsquo;t have.. at least until now. The trick people use is Q4 quantization, which basically compresses the model down significantly. The 31B at Q4 can fit around 16-17 GB.. still a bit above what I got but getting closer. Maybe next GPU.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-laptop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;on-mobile&#34;&gt;On mobile&lt;/h2&gt;\n&lt;p&gt;It runs surprisingly fast and remains fairly accurate. The output isn&amp;rsquo;t exactly the same as the desktop models, largely because of how mobile inference is optimized. Instead of performing full, heavy computations on every layer from scratch for every word, mobile execution uses techniques like quantization and KV caching. Essentially, the model reuses pre-calculated states (like a memory map) on each layer for the context it has already processed, which drastically speeds up generation on phones. Because of these aggressive optimizations, it&amp;rsquo;s hard to measure its accuracy strictly against the uncompressed desktop versions. Still, to give it credit, it handles an impressive 140 languages easily; heck, I even used my native language and it worked great when I tried it.&lt;/p&gt;\n&lt;p&gt;To run these models locally, I am using &lt;strong&gt;&lt;a href=&#34;https://github.com/google-ai-edge/gallery&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google AI Edge Gallery&lt;/a&gt;&lt;/strong&gt;. It is an open-source, experimental mobile application from Google that showcases running Large Language Models entirely on-device (Android and iOS). Because the app uses &lt;a href=&#34;https://ai.google.dev/edge/litert&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiteRT&lt;/a&gt; (formerly TensorFlow Lite) and the &lt;a href=&#34;https://ai.google.dev/edge/mediapipe/solutions/genai/llm_inference&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LLM Inference API&lt;/a&gt;, the models execute offline directly on the phone&amp;rsquo;s hardware. This means complete data privacy with no cloud API costs. The app provides great features out of the box like LLM Chat, multimodal interactions to ask questions about images, a Prompt Lab for testing ideas locally.. and much more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-mobile.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal&#34;&gt;personal&lt;/h2&gt;\n&lt;p&gt;Hey, how are you doing? I hope everything is going well. I lost my iPhone this month; it was hectic. I lost it when traveling back from the office to my Chennai home. Although I have had the phone for a while now, and it&amp;rsquo;s old.. it still stings. My first time losing a phone. Why would I lose an iPhone? It was a series of events that happened that day. A few days before.. I accidentally bent the new glasses I got. Then during that day, the glasses got so loose that I couldn&amp;rsquo;t wear them the entire day. The temples (new term.. weird to call that piece that.. anyway) were so loose, they would have come into two pieces if I had worn them that day. So before that, a day ago, my mom got into a little accident. All of these in mind, I got into a weird headspace and it led to me losing the phone. I felt something drop.. but when I looked for it, it was still there where I kept the phone. On top of it, the pouch that I kept the phone in was torn a bit and I hadn&amp;rsquo;t changed it.. which also contributed to this. I&amp;rsquo;m never keeping a phone in a pouch again! ..that&amp;rsquo;s what I told myself, but future me will catch myself doing the same in the future, I think. After that.. I lost my building entrance card. My office card I had, but I lost the Olympia Guindy building card, which is another pain.. but at the time of updating this, on April 15, it was found and I went and collected it.&lt;/p&gt;\n&lt;p&gt;It&amp;rsquo;s been a long time since I bought a phone. Before, I used to develop for Android and iOS; now I don&amp;rsquo;t anymore. I don&amp;rsquo;t think I need an iPhone again.. a better deal is to buy a latest &lt;a href=&#34;https://www.qualcomm.com/snapdragon&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Snapdragon chipset&lt;/a&gt; phone and call it a day. Going to switch back to bigger XL screen phones.&lt;/p&gt;\n&lt;p&gt;Lots of things to do, yet you choose to have little moments of fun? At the moment, my brain can&amp;rsquo;t remember what pending things are left.&lt;/p&gt;\n&lt;p&gt;A friend stayed over for a while at the start of April. I had to work on weekends because of horrible management in the office. I try to do a small piece of work every day though, but still.. yeah. He was going on a trip.. north and back and stayed for a while. The initial plan was that he would stay here for a while, but then we would both go to our hometown. He is a childhood friend of mine, but work got hectic; I couldn&amp;rsquo;t make it as discussed next week and he had to go alone.&lt;/p&gt;\n&lt;p&gt;Last year at this time, I was playing &lt;a href=&#34;https://www.playstation.com/games/ghost-of-tsushima/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Ghost of Tsushima&lt;/a&gt; in my off-time. This year, I&amp;rsquo;m playing &lt;a href=&#34;https://megacrit.com/slaythespire2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Slay the Spire 2&lt;/a&gt; and &lt;a href=&#34;https://crimsondesert.pearlabyss.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Crimson Desert&lt;/a&gt; (released on March 19th, 2026) in my off-time.. dang nab it, I should give up this gaming.. I&amp;rsquo;m getting old.. but what if that changes me entirely? Risky..&lt;/p&gt;</description></item><item><title>&#39;AI Skills&#39;</title><link>//bluepie.in/2026/03/mar-2026/</link><pubDate>Tue, 03 Mar 2026 05:24:25 +0000</pubDate><guid>//bluepie.in/2026/03/mar-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Anthropic came and published their skills documentation on what they are roughly 2 months ago, but I suspect they and other AI companies were using them internally even before they published. The idea of people seeing them as a new thing and being astonished is abysmal..because we were already using it..but without the naming convention and the optimised context limiting. The concept is simple: you create a folder named skill you want, then inside it, the main file is called skill.md. This takes less context when ai reads it, as it just reads the frontmatter alone; you write detailed instructions below the frontmatter with different scripts and whatever that is necessary to finish that task/skill, you also writter a condition upon which the ai will trigger. The AI then keeps all these details and uses it ad hoc. The problem it solves is that this uses way less context than mcp or others and only triggers when necessary.&lt;/p&gt;\n&lt;h2 id=&#34;example-plantuml-generator&#34;&gt;Example: PlantUML Generator&lt;/h2&gt;\n&lt;p&gt;So the idea is simple; as you can infer from above, let&amp;rsquo;s go with an example. I wanted to create an architecture diagram skill in office. ..in office we use he PlantUML to create sequence diagrams.&lt;/p&gt;\n&lt;p&gt;The problem is you have to ask the AI to generate it, and then generate the image and maintain the image somewhere. The problem with this is it takes time and its a manual process. How can we use skills to automate this? We can write instructions to ask the AI chat / agent to be like, if a user asks x, then do the process and update the diagram. We can add these instructions in the skill.md file, keep the name and descriptions to work with the architecture diagram.&lt;/p&gt;\n&lt;p&gt;You can write scripts and then use them to generate things; the AI will execute them based on the instructions. The next is dependencies, if any. Since PlantUML is an open-source project, so we can download the jar file and put that in the skills folder. since its a java project..it&amp;rsquo;s platform independent, so no need to worry about different binaries&lt;/p&gt;\n&lt;p&gt;So everything can be packaged into a single skill.&lt;/p&gt;\n&lt;p&gt;you can see the skills folder here: &lt;a href=&#34;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;embedding-code-in-images&#34;&gt;Embedding Code in Images&lt;/h2&gt;\n&lt;p&gt;On top of the skill, I was thinking about for a optimization. In college, I conducted a Capture the Flag; the event was like Google CTF and other CTF events from that time. I was inspired by them, Anyway. one of the competitions was to find the hidden details from the image. Inside an image, you can add a data to it, it can be any data. So I was thinking, why can&amp;rsquo;t we use the same thing here? So I asked ai to write a script to embed code into the images. So the PlantUML diagram is stored inside the images, the images are the output of the code. It&amp;rsquo;s like a chicken and egg problem once you delete the code file from the repo.&lt;/p&gt;\n&lt;p&gt;We want crud options on this; the update image, edit image, and create image are just a set of instructions that the AI can read and execute.. in my case it will be Python scripts, when user asks to edit, it will extract the code from the image and create a temporary file we can work on, and when we ask to store it, it saves the source code, again inside the image, deleting the code file.&lt;/p&gt;\n&lt;h2 id=&#34;other-skills&#34;&gt;Other Skills&lt;/h2&gt;\n&lt;p&gt;There are other skills that I use here, it keeps changing overtime.&lt;/p&gt;\n&lt;p&gt;The main one I wanted to share is the PlantUML generator; it has all the features that I need to be adding to demonstrate the features of the skill.&lt;/p&gt;\n&lt;h2 id=&#34;sub-agents-lifecycle-hooks&#34;&gt;Sub-agents &amp;amp; Lifecycle Hooks&lt;/h2&gt;\n&lt;p&gt;At the end of Feb 2026, the Claude team updated the skillset to have custom features, like sub-agents. What it is, is that the sub-agent is being called by the main agent when doing a certain task.. so it will fix the context problem. it&amp;rsquo;s one of the major updates to skills; it&amp;rsquo;s the major one that makes a significant difference, because it won&amp;rsquo;t clutter the main context; the main agent is not interfered with.&lt;/p&gt;\n&lt;p&gt;It also has access to the lifecycle events, Lifecycle hooks (on_start, on_success, on_error, PreToolUse, etc.).. but i haven&amp;rsquo;t adopted them yet; but this will help me with the cryo vault project: &lt;a href=&#34;https://github.com/aghontpi/cryo-vault&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/cryo-vault&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m having to juggle a lot of things in my life currently; finding focus time to think on what is essential or next is hard with this attention-seeking world; socializing; maintaining health and ad hoc new plans; argh! I started to have a new habit; hopefully, when I am consistent on this.. I&amp;rsquo;ll post on this later!.. future me..! what is happening? hope you are good!&lt;/p&gt;</description></item><item><title>Selfhosting an Automation tool - N8N</title><link>//bluepie.in/2026/02/feb-2026-2/</link><pubDate>Sat, 28 Feb 2026 15:17:41 +0000</pubDate><guid>//bluepie.in/2026/02/feb-2026-2/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;In the previous post, I was talking about &lt;a href=&#34;https://claude.com/skills&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;claude&lt;/a&gt; skills. This is about automation, no code/ minimal solutions. At this point, it&amp;rsquo;s been over 1.5 to 2 years since &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and automation; nowadays it&amp;rsquo;s easy to just build an app/ ask AI to build the app instead of creating the automation, it&amp;rsquo;s easier that way, but regardless, let&amp;rsquo;s see how the future goes; I&amp;rsquo;m already seeing the trend of creating custom apps for each one&amp;rsquo;s personal preference.&lt;/p&gt;\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;Instead of creating projects for each use and hosting it for every use case, I thought of using automation tools and solving those problems; for ex, one such problem is creating a diet maintainer; I ranted in a previous post that I gained a couple of kgs in a short while, but I&amp;rsquo;m trying to reduce it; so I have to keep track of the amount of calories, etc., but I want this to be an Excel sheet for tracking, yes there are apps for that; but I wanted a simple chat interface where I can type the food I ate and when I ate it, and it should approximate the calories, and when I ask how much is left, it should give back the amount of food that I can eat in the remaining part of the day/ from that point onwards.&lt;/p&gt;\n&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;\n&lt;p&gt;I choose n8n. The main reason I prefer it is the immense community support it has, it&amp;rsquo;s incredibly popular, which means if I run into a wall, someone else has probably already solved it and posted about it. Plus, being able to tinker with it under the hood while self-hosting is a massive bonus compared to locked-in cloud alternatives.&lt;/p&gt;\n&lt;p&gt;Speaking of alternatives, there are quite a few out there:\n- &lt;a href=&#34;https://www.make.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Make (formerly Integromat)&lt;/a&gt;: Great visual builder,\n- &lt;a href=&#34;https://zapier.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Zapier&lt;/a&gt;: The biggest name out there with the most integrations,\n- &lt;a href=&#34;https://pipedream.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Pipedream&lt;/a&gt;: Fantastic for developers who want to write code snippets, but maybe overkill if you just want pure drag-and-drop.\n- &lt;a href=&#34;https://www.activepieces.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Activepieces&lt;/a&gt;: is similar to n8n&lt;/p&gt;\n&lt;h2 id=&#34;self-hosting&#34;&gt;Self hosting&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Here is the official n8n documentation guide&lt;/a&gt;, I started to see the official docs of n8n. One thing I got surprised by is that they have incorporated AI, and that AI is surprisingly good at providing the right instructions, because when you host with different servers, server config differs for each one like &lt;a href=&#34;https://caddyserver.com/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;caddy&lt;/a&gt; / &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;/ etc., and for automation to work, it has to be able to receive webhooks, and alongside things like if it&amp;rsquo;s behind a proxy; it must be configured to tell that you are behind a proxy, and based on that it can handle the incoming request, etc.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll save you the trouble and give the config here; basically I was able to set up the Nginx server pretty easily using the AI bot that they have incorporated in their site. Below is all the config I needed to be able to run this from &lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; and &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;:&lt;/p&gt;\n&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I like to run things with &lt;a href=&#34;https://docs.podman.io/en/latest/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;podman&lt;/a&gt;/&lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; as it removes the dependency from my cloud machine.&lt;/p&gt;\n&lt;p&gt;Obviously I have to configure my domain name to &lt;a href=&#34;https://www.godaddy.com/help&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;godaddy&lt;/a&gt;/&lt;a href=&#34;https://developers.cloudflare.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt; and deploy SSL certificates; I&amp;rsquo;ve covered that plenty in my other posts:&lt;/p&gt;\n&lt;p&gt;docker config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;podman run -d --restart unless-stopped \\\n--name n8n \\\n-p 5678:5678 \\\n-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \\\n-e N8N_RUNNERS_ENABLED=true \\\n-e WEBHOOK_URL=https://&amp;lt;server-name&amp;gt;/ \\\n-e N8N_PROXY_HOPS=1 \\\n-e N8N_HOST=&amp;lt;server-name&amp;gt;\n-e N8N_PORT=5678 \\\n-e N8N_PROTOCOL=https \\\n-v /mnt/podman-data/n8n_data:/home/node/.n8n:Z \\\ndocker.n8n.io/n8nio/n8n\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;nginx config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;server {\nlisten 80;\nserver_name &amp;lt;server-name&amp;gt;\nlocation / {\nproxy_pass http://127.0.0.1:5678;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_set_header X-Forwarded-Port $server_port;\nchunked_transfer_encoding off;\nproxy_buffering off;\nproxy_cache off;\n}\nlocation /webhook/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\nlocation /webhook-test/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-how-to-configure&#34;&gt;Configuring - How to configure&lt;/h2&gt;\n&lt;p&gt;Modifying a telegram bot to track the calory intake of the day based on estimates from me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/overview.png&#34; alt=&#34;Overview&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is initial learning time to get used to what each option is, but when you get the hang of it, it&amp;rsquo;s pretty easy, since I&amp;rsquo;ve done drag and drop editors before; I caught up easily in approx 5-10ish mins.&lt;/p&gt;\n&lt;p&gt;Create a &lt;a href=&#34;https://core.telegram.org/bots/api&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;telegram&lt;/a&gt; node and select the appropriate Telegram account credential. Set the &lt;strong&gt;Trigger On&lt;/strong&gt; parameter to &lt;code&gt;Message&lt;/code&gt; so it triggers on new incoming messages of any kind (text, photo, sticker, etc.). Note that due to Telegram API limitations, you can only use one Telegram trigger per bot. Also ensure there is validation to only process messages from your known user ID, otherwise the bot will refuse to respond.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/telegram-trigger-receive.png&#34; alt=&#34;Telegram Trigger Settings&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Add an &lt;strong&gt;Analyze Text Message&lt;/strong&gt; node and connect it to your Google Gemini (or equivalent API) configuration. Set the Resource to &lt;code&gt;Text&lt;/code&gt; and Operation to &lt;code&gt;Message a Model&lt;/code&gt;, using the &lt;code&gt;models/gemini-2.5-flash&lt;/code&gt; model so its fast and reliable. models cheaper than 2.5-flash are not reliable based on testing. Within the node, establish a system prompt (Role: &lt;code&gt;Model&lt;/code&gt;) instructing the AI to process user input into structured nutritional data—identifying meal components, estimating portion weights, and warning it not to reveal its internal steps. Pass the user input as a prompt (Role: &lt;code&gt;User&lt;/code&gt;) using the expression &lt;code&gt;{{ $json.message }}&lt;/code&gt; along with today&amp;rsquo;s date context: &lt;code&gt;{{ $today.format(&#39;yyyy-MM-dd&#39;) }}&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/using-gemini-ai-via-api.png&#34; alt=&#34;AI Processing Configuration&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;To store the parsed responses, add a Google Sheets node configured to &amp;ldquo;Append&amp;rdquo; rows, and set it to &lt;strong&gt;Map Each Column Manually&lt;/strong&gt;. You can map fields originating from the trigger and model response:\n- &lt;strong&gt;User_ID&lt;/strong&gt;: &lt;code&gt;{{ $json.chat_id }}&lt;/code&gt;\n- &lt;strong&gt;Date&lt;/strong&gt;: Extract and format the date directly with &lt;code&gt;{{ DateTime.fromSeconds($json.date).format(&#39;yyyy-LL-d&#39;) }}&lt;/code&gt;.\n- Dietary metrics like &lt;strong&gt;Meal_description&lt;/strong&gt;, &lt;strong&gt;Calories&lt;/strong&gt;, &lt;strong&gt;Proteins&lt;/strong&gt;, &lt;strong&gt;Carbs&lt;/strong&gt;, and &lt;strong&gt;Fats&lt;/strong&gt; can be defined automatically by mapping them from structured outputs generated by the AI model.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/append-to-excel-sheet.png&#34; alt=&#34;Append to Google Sheets&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is all sorts of existing nodes (in &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;, a &amp;ldquo;node&amp;rdquo; is basically a building block or a step in your automation workflow—it can be a trigger that starts the flow, an action that connects to a specific app&amp;rsquo;s API like sending a message, or a logic step that filters or processes data) that we can use. For this use case, reusing the &lt;a href=&#34;https://developers.google.com/sheets/api/guides/concepts&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google sheet&lt;/a&gt; node from official requires getting API keys, but that is well documented by &lt;a href=&#34;https://docs.n8n.io/integrations/builtin/credentials/google/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and &lt;a href=&#34;https://developers.google.com/workspace/guides/create-credentials&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;Finally, configure another Telegram node to send a reply back to the user. Set the Resource to &lt;code&gt;Message&lt;/code&gt; and Operation to &lt;code&gt;Send Message&lt;/code&gt;. For the &lt;strong&gt;Chat ID&lt;/strong&gt;, retrieve it from the initial trigger using &lt;code&gt;{{ $(&#39;Telegram Trigger&#39;).item.json.message.chat.id }}&lt;/code&gt;. Include the formatted Text from the AI calculation &lt;code&gt;{{ $json.message }}&lt;/code&gt;. Under Additional Fields, be sure to set &lt;strong&gt;Parse Mode&lt;/strong&gt; to &lt;code&gt;MarkdownV2&lt;/code&gt; so any formatting from the AI looks correct, and toggle off &amp;ldquo;Append n8n Attribution&amp;rdquo; for a cleaner message.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/send-telegram-msg-back.png&#34; alt=&#34;Send Telegram Summary Message&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For the initial setup, because it&amp;rsquo;s new, I was experimenting and took 4-5 hours, because it was interesting; but been using that for maintaining calories now; it&amp;rsquo;s great.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;AI is more prevalent these days, instead of building these automation no code solutions via these tools, users directly build with AI, there are pros and cons.. it&amp;rsquo;s already deployed on the server (the automation tool, one time setup), can deploy any number of times.. limit depends on the server limits, I&amp;rsquo;m okay.. but people need to know of how to debug in case of error, I mean there is AI for that too; if using &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;/automation.. everything is contained within this little confinement work area.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll be trying out other automation experiments, this is serving me well, recently I gained a lot of weight, though not overweight.. I still look fit from an outside view; but a little fat in stomach and back.. I started eating snacks every night and lots of oats (used to prepare the previous night with milk and sugar.. keep it in freezer).. the next morning eating it, I drove this diet for some time, it was good for my morning routine (not going to office on an empty stomach), but the problem was I gained like 5 kgs, I have never been this much weight in my entire life! but I&amp;rsquo;m 6ft.. so the weight is kinda explainable.&lt;/p&gt;\n&lt;p&gt;Me when 76kgs, ideal for my height, less fat content ratio in my body; this is before I got muscle buildup.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/me-when-76.jpg&#34; alt=&#34;image when I was 76kgs&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;but in terms of physique, , I got visibly more muscle development..after above image, idk.. started to go to gym again, let&amp;rsquo;s see, till now I&amp;rsquo;ve been continuing to do it every day except Sunday at home. But I&amp;rsquo;ll track the progress.&lt;/p&gt;\n&lt;p&gt;Updating this part as of March 2026, it&amp;rsquo;s happening again, I feel out of place, everyone around here is behind.. so behind .. than I expect them to be in, when I see, I see a fine line between things and it&amp;rsquo;s like I can slip in between them.. and go past beyond. I see it like this, a highway road that is busy with vehicles going left and right, but I see a path in slow motion that I can slip in between and move to the other side, I see what others are not seeing, IDK how long I will be blessed with this gift or curse that I have, can&amp;rsquo;t share this with anyone, I exposed this lil bit to sq#3, ~Future me is this happening again? Hope you landed on things, learned from past.&lt;/p&gt;</description></item><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item></channel></rss>"}, "79511b94513d35212fc5b753841cea70a131317a": {"t": 1785770737.3095539, "n": 161518, "h": "4755a2effcfc643f", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sun, 12 Apr 2026 05:49:01 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Gemma4 - Easily running on phones - April 2026</title><link>//bluepie.in/2026/04/april-2026/</link><pubDate>Sun, 12 Apr 2026 05:49:01 +0000</pubDate><guid>//bluepie.in/2026/04/april-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Today I want to talk about, &lt;a href=&#34;https://ai.google.dev/gemma/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Gemma 4&lt;/a&gt;; this sets a new standard for source AI models. I am astonished by the quality of this, though it has a small model size compared to the other big ones out there.. like &lt;a href=&#34;https://kimi.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Kimi K2.5 from Moonshot AI&lt;/a&gt;, that thing is a 1 trillion parameter model.. 1 trillion! and Gemma 4&amp;rsquo;s biggest is only 31 billion.. but here is the thing, Kimi K2.5 only uses 32 billion of those parameters at any given time, so they are not that far apart in practice.. and Gemma 4 punches way above its weight for how small it actually is..&lt;/p&gt;\n&lt;h2 id=&#34;gemma4&#34;&gt;gemma4&lt;/h2&gt;\n&lt;p&gt;In the Gemma models, they always release with active and passive parameters being announced in the model name. They do this by mentioning a and e. There is also a dense model which loads the complete model. So.. the &lt;code&gt;e&lt;/code&gt; stands for effective, like &lt;code&gt;E2B&lt;/code&gt; or &lt;code&gt;E4B&lt;/code&gt;.. that means it is a Mixture of Experts model and the number is how many parameters are actually active per token. Think of it like a restaurant with 26 chefs.. but only 4 of them are actually cooking your order at any point, the rest are just standing by with their own specialization, so it runs fast like a 4B model but it has the knowledge of a full 26B one.. pretty clever. The &lt;code&gt;a&lt;/code&gt; is used similarly in some naming, same idea basically. And then there is the dense model.. that one just loads everything, all 31 billion parameters fire for every single token, no routing no tricks, just brute force.. uses more memory but more predictable&lt;/p&gt;\n&lt;p&gt;There are two classifications of models, and in each of these classifications there are multiple models that are released. Two of the four.. are for desktop/laptop, and the others, they created for edge devices. What they mean by edge devices are mobile phones, Raspberry Pi, and other less powerful devices or mobile.&lt;/p&gt;\n&lt;h2 id=&#34;on-pc&#34;&gt;On PC&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been a long time user of &lt;a href=&#34;https://lmstudio.ai/docs&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;lm studio&lt;/a&gt;, it offers great features.. I like it because it offers ease of use, ability to manage different models all at once,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/lm-studio-ui.webp&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;from the official documentation. I can run up to 14 GB of graphics memory worth of model. The 31B dense model needs around 20+ GB at full precision.. so that is out for me. The 26B MoE is interesting though.. even though it has 26B total params you only compute 4B at a time, so it is lighter on the processor, but you still have to load all 26B into VRAM which I don&amp;rsquo;t have.. at least until now. The trick people use is Q4 quantization, which basically compresses the model down significantly. The 31B at Q4 can fit around 16-17 GB.. still a bit above what I got but getting closer. Maybe next GPU.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-laptop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;on-mobile&#34;&gt;On mobile&lt;/h2&gt;\n&lt;p&gt;Runs surprisingly fast and accurate to a degree. It&amp;rsquo;s not the same as the desktop models, because.. for the model, when run on each layer, the layers are precalculated. They are being called as.. instead of doing computation on each layer, they are reusing a map on each layer; that is what is making the mobile devices run faster. Because of this, it&amp;rsquo;s hard to measure accuracy on this. To give it credit, it does have the ability to recognize 140 languages easily; heck, I even used my native language and it worked great when I tried it.&lt;/p&gt;\n&lt;p&gt;To run these models locally, I am using &lt;strong&gt;&lt;a href=&#34;https://github.com/google-ai-edge/gallery&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google AI Edge Gallery&lt;/a&gt;&lt;/strong&gt;. It is an open-source, experimental mobile application from Google that showcases running Large Language Models entirely on-device (Android and iOS). Because the app uses &lt;a href=&#34;https://ai.google.dev/edge/litert&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiteRT&lt;/a&gt; (formerly TensorFlow Lite) and the &lt;a href=&#34;https://ai.google.dev/edge/mediapipe/solutions/genai/llm_inference&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LLM Inference API&lt;/a&gt;, the models execute offline directly on the phone&amp;rsquo;s hardware. This means complete data privacy with no cloud API costs. The app provides great features out of the box like LLM Chat, multimodal interactions to ask questions about images, a Prompt Lab for testing ideas locally.. and much more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2026/gemma4-on-mobile.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal&#34;&gt;personal&lt;/h2&gt;\n&lt;p&gt;Hey, how are you doing? I hope everything is going well. I lost my iPhone this month; it was hectic. I lost it when traveling back from the office to my Chennai home. Although I have had the phone for a while now, and it&amp;rsquo;s old.. it still stings. My first time losing a phone. Why would I lose an iPhone? It was a series of events that happened that day. A few days before.. I accidentally bent the new glasses I got. Then during that day, the glasses got so loose that I couldn&amp;rsquo;t wear them the entire day. The temples (new term.. weird to call that piece that.. anyway) were so loose, they would have come into two pieces if I had worn them that day. So before that, a day ago, my mom got into a little accident. All of these in mind, I got into a weird headspace and it led to me losing the phone. I felt something drop.. but when I looked for it, it was still there where I kept the phone. On top of it, the pouch that I kept the phone in was torn a bit and I hadn&amp;rsquo;t changed it.. which also contributed to this. I&amp;rsquo;m never keeping a phone in a pouch again! ..that&amp;rsquo;s what I told myself, but future me will catch myself doing the same in the future, I think. After that.. I lost my building entrance card. My office card I had, but I lost the Olympia Guindy building card, which is another pain.. but at the time of updating this, on April 15, it was found and I went and collected it.&lt;/p&gt;\n&lt;p&gt;It&amp;rsquo;s been a long time since I bought a phone. Before, I used to develop for Android and iOS; now I don&amp;rsquo;t anymore. I don&amp;rsquo;t think I need an iPhone again.. a better deal is to buy a latest &lt;a href=&#34;https://www.qualcomm.com/snapdragon&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Snapdragon chipset&lt;/a&gt; phone and call it a day. Going to switch back to bigger XL screen phones.&lt;/p&gt;\n&lt;p&gt;Lots of things to do, yet you choose to have little moments of fun? At the moment, my brain can&amp;rsquo;t remember what pending things are left.&lt;/p&gt;\n&lt;p&gt;A friend stayed over for a while at the start of April. I had to work on weekends because of horrible management in the office. I try to do a small piece of work every day though, but still.. yeah. He was going on a trip.. north and back and stayed for a while. The initial plan was that he would stay here for a while, but then we would both go to our hometown. He is a childhood friend of mine, but work got hectic; I couldn&amp;rsquo;t make it as discussed next week and he had to go alone.&lt;/p&gt;\n&lt;p&gt;Last year at this time, I was playing &lt;a href=&#34;https://www.playstation.com/games/ghost-of-tsushima/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Ghost of Tsushima&lt;/a&gt; in my off-time. This year, I&amp;rsquo;m playing &lt;a href=&#34;https://megacrit.com/slaythespire2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Slay the Spire 2&lt;/a&gt; and &lt;a href=&#34;https://crimsondesert.pearlabyss.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Crimson Desert&lt;/a&gt; (released on March 19th, 2026) in my off-time.. dang nab it, I should give up this gaming.. I&amp;rsquo;m getting old.. but what if that changes me entirely? Risky..&lt;/p&gt;</description></item><item><title>&#39;AI Skills&#39;</title><link>//bluepie.in/2026/03/mar-2026/</link><pubDate>Tue, 03 Mar 2026 05:24:25 +0000</pubDate><guid>//bluepie.in/2026/03/mar-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Anthropic came and published their skills documentation on what they are roughly 2 months ago, but I suspect they and other AI companies were using them internally even before they published. The idea of people seeing them as a new thing and being astonished is abysmal..because we were already using it..but without the naming convention and the optimised context limiting. The concept is simple: you create a folder named skill you want, then inside it, the main file is called skill.md. This takes less context when ai reads it, as it just reads the frontmatter alone; you write detailed instructions below the frontmatter with different scripts and whatever that is necessary to finish that task/skill, you also writter a condition upon which the ai will trigger. The AI then keeps all these details and uses it ad hoc. The problem it solves is that this uses way less context than mcp or others and only triggers when necessary.&lt;/p&gt;\n&lt;h2 id=&#34;example-plantuml-generator&#34;&gt;Example: PlantUML Generator&lt;/h2&gt;\n&lt;p&gt;So the idea is simple; as you can infer from above, let&amp;rsquo;s go with an example. I wanted to create an architecture diagram skill in office. ..in office we use he PlantUML to create sequence diagrams.&lt;/p&gt;\n&lt;p&gt;The problem is you have to ask the AI to generate it, and then generate the image and maintain the image somewhere. The problem with this is it takes time and its a manual process. How can we use skills to automate this? We can write instructions to ask the AI chat / agent to be like, if a user asks x, then do the process and update the diagram. We can add these instructions in the skill.md file, keep the name and descriptions to work with the architecture diagram.&lt;/p&gt;\n&lt;p&gt;You can write scripts and then use them to generate things; the AI will execute them based on the instructions. The next is dependencies, if any. Since PlantUML is an open-source project, so we can download the jar file and put that in the skills folder. since its a java project..it&amp;rsquo;s platform independent, so no need to worry about different binaries&lt;/p&gt;\n&lt;p&gt;So everything can be packaged into a single skill.&lt;/p&gt;\n&lt;p&gt;you can see the skills folder here: &lt;a href=&#34;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;embedding-code-in-images&#34;&gt;Embedding Code in Images&lt;/h2&gt;\n&lt;p&gt;On top of the skill, I was thinking about for a optimization. In college, I conducted a Capture the Flag; the event was like Google CTF and other CTF events from that time. I was inspired by them, Anyway. one of the competitions was to find the hidden details from the image. Inside an image, you can add a data to it, it can be any data. So I was thinking, why can&amp;rsquo;t we use the same thing here? So I asked ai to write a script to embed code into the images. So the PlantUML diagram is stored inside the images, the images are the output of the code. It&amp;rsquo;s like a chicken and egg problem once you delete the code file from the repo.&lt;/p&gt;\n&lt;p&gt;We want crud options on this; the update image, edit image, and create image are just a set of instructions that the AI can read and execute.. in my case it will be Python scripts, when user asks to edit, it will extract the code from the image and create a temporary file we can work on, and when we ask to store it, it saves the source code, again inside the image, deleting the code file.&lt;/p&gt;\n&lt;h2 id=&#34;other-skills&#34;&gt;Other Skills&lt;/h2&gt;\n&lt;p&gt;There are other skills that I use here, it keeps changing overtime.&lt;/p&gt;\n&lt;p&gt;The main one I wanted to share is the PlantUML generator; it has all the features that I need to be adding to demonstrate the features of the skill.&lt;/p&gt;\n&lt;h2 id=&#34;sub-agents-lifecycle-hooks&#34;&gt;Sub-agents &amp;amp; Lifecycle Hooks&lt;/h2&gt;\n&lt;p&gt;At the end of Feb 2026, the Claude team updated the skillset to have custom features, like sub-agents. What it is, is that the sub-agent is being called by the main agent when doing a certain task.. so it will fix the context problem. it&amp;rsquo;s one of the major updates to skills; it&amp;rsquo;s the major one that makes a significant difference, because it won&amp;rsquo;t clutter the main context; the main agent is not interfered with.&lt;/p&gt;\n&lt;p&gt;It also has access to the lifecycle events, Lifecycle hooks (on_start, on_success, on_error, PreToolUse, etc.).. but i haven&amp;rsquo;t adopted them yet; but this will help me with the cryo vault project: &lt;a href=&#34;https://github.com/aghontpi/cryo-vault&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/cryo-vault&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m having to juggle a lot of things in my life currently; finding focus time to think on what is essential or next is hard with this attention-seeking world; socializing; maintaining health and ad hoc new plans; argh! I started to have a new habit; hopefully, when I am consistent on this.. I&amp;rsquo;ll post on this later!.. future me..! what is happening? hope you are good!&lt;/p&gt;</description></item><item><title>Selfhosting an Automation tool - N8N</title><link>//bluepie.in/2026/02/feb-2026-2/</link><pubDate>Sat, 28 Feb 2026 15:17:41 +0000</pubDate><guid>//bluepie.in/2026/02/feb-2026-2/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;In the previous post, I was talking about &lt;a href=&#34;https://claude.com/skills&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;claude&lt;/a&gt; skills. This is about automation, no code/ minimal solutions. At this point, it&amp;rsquo;s been over 1.5 to 2 years since &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and automation; nowadays it&amp;rsquo;s easy to just build an app/ ask AI to build the app instead of creating the automation, it&amp;rsquo;s easier that way, but regardless, let&amp;rsquo;s see how the future goes; I&amp;rsquo;m already seeing the trend of creating custom apps for each one&amp;rsquo;s personal preference.&lt;/p&gt;\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;Instead of creating projects for each use and hosting it for every use case, I thought of using automation tools and solving those problems; for ex, one such problem is creating a diet maintainer; I ranted in a previous post that I gained a couple of kgs in a short while, but I&amp;rsquo;m trying to reduce it; so I have to keep track of the amount of calories, etc., but I want this to be an Excel sheet for tracking, yes there are apps for that; but I wanted a simple chat interface where I can type the food I ate and when I ate it, and it should approximate the calories, and when I ask how much is left, it should give back the amount of food that I can eat in the remaining part of the day/ from that point onwards.&lt;/p&gt;\n&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;\n&lt;p&gt;I choose n8n. The main reason I prefer it is the immense community support it has, it&amp;rsquo;s incredibly popular, which means if I run into a wall, someone else has probably already solved it and posted about it. Plus, being able to tinker with it under the hood while self-hosting is a massive bonus compared to locked-in cloud alternatives.&lt;/p&gt;\n&lt;p&gt;Speaking of alternatives, there are quite a few out there:\n- &lt;a href=&#34;https://www.make.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Make (formerly Integromat)&lt;/a&gt;: Great visual builder,\n- &lt;a href=&#34;https://zapier.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Zapier&lt;/a&gt;: The biggest name out there with the most integrations,\n- &lt;a href=&#34;https://pipedream.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Pipedream&lt;/a&gt;: Fantastic for developers who want to write code snippets, but maybe overkill if you just want pure drag-and-drop.\n- &lt;a href=&#34;https://www.activepieces.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Activepieces&lt;/a&gt;: is similar to n8n&lt;/p&gt;\n&lt;h2 id=&#34;self-hosting&#34;&gt;Self hosting&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Here is the official n8n documentation guide&lt;/a&gt;, I started to see the official docs of n8n. One thing I got surprised by is that they have incorporated AI, and that AI is surprisingly good at providing the right instructions, because when you host with different servers, server config differs for each one like &lt;a href=&#34;https://caddyserver.com/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;caddy&lt;/a&gt; / &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;/ etc., and for automation to work, it has to be able to receive webhooks, and alongside things like if it&amp;rsquo;s behind a proxy; it must be configured to tell that you are behind a proxy, and based on that it can handle the incoming request, etc.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll save you the trouble and give the config here; basically I was able to set up the Nginx server pretty easily using the AI bot that they have incorporated in their site. Below is all the config I needed to be able to run this from &lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; and &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;:&lt;/p&gt;\n&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I like to run things with &lt;a href=&#34;https://docs.podman.io/en/latest/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;podman&lt;/a&gt;/&lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; as it removes the dependency from my cloud machine.&lt;/p&gt;\n&lt;p&gt;Obviously I have to configure my domain name to &lt;a href=&#34;https://www.godaddy.com/help&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;godaddy&lt;/a&gt;/&lt;a href=&#34;https://developers.cloudflare.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt; and deploy SSL certificates; I&amp;rsquo;ve covered that plenty in my other posts:&lt;/p&gt;\n&lt;p&gt;docker config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;podman run -d --restart unless-stopped \\\n--name n8n \\\n-p 5678:5678 \\\n-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \\\n-e N8N_RUNNERS_ENABLED=true \\\n-e WEBHOOK_URL=https://&amp;lt;server-name&amp;gt;/ \\\n-e N8N_PROXY_HOPS=1 \\\n-e N8N_HOST=&amp;lt;server-name&amp;gt;\n-e N8N_PORT=5678 \\\n-e N8N_PROTOCOL=https \\\n-v /mnt/podman-data/n8n_data:/home/node/.n8n:Z \\\ndocker.n8n.io/n8nio/n8n\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;nginx config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;server {\nlisten 80;\nserver_name &amp;lt;server-name&amp;gt;\nlocation / {\nproxy_pass http://127.0.0.1:5678;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_set_header X-Forwarded-Port $server_port;\nchunked_transfer_encoding off;\nproxy_buffering off;\nproxy_cache off;\n}\nlocation /webhook/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\nlocation /webhook-test/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-how-to-configure&#34;&gt;Configuring - How to configure&lt;/h2&gt;\n&lt;p&gt;Modifying a telegram bot to track the calory intake of the day based on estimates from me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/overview.png&#34; alt=&#34;Overview&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is initial learning time to get used to what each option is, but when you get the hang of it, it&amp;rsquo;s pretty easy, since I&amp;rsquo;ve done drag and drop editors before; I caught up easily in approx 5-10ish mins.&lt;/p&gt;\n&lt;p&gt;Create a &lt;a href=&#34;https://core.telegram.org/bots/api&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;telegram&lt;/a&gt; node and select the appropriate Telegram account credential. Set the &lt;strong&gt;Trigger On&lt;/strong&gt; parameter to &lt;code&gt;Message&lt;/code&gt; so it triggers on new incoming messages of any kind (text, photo, sticker, etc.). Note that due to Telegram API limitations, you can only use one Telegram trigger per bot. Also ensure there is validation to only process messages from your known user ID, otherwise the bot will refuse to respond.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/telegram-trigger-receive.png&#34; alt=&#34;Telegram Trigger Settings&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Add an &lt;strong&gt;Analyze Text Message&lt;/strong&gt; node and connect it to your Google Gemini (or equivalent API) configuration. Set the Resource to &lt;code&gt;Text&lt;/code&gt; and Operation to &lt;code&gt;Message a Model&lt;/code&gt;, using the &lt;code&gt;models/gemini-2.5-flash&lt;/code&gt; model so its fast and reliable. models cheaper than 2.5-flash are not reliable based on testing. Within the node, establish a system prompt (Role: &lt;code&gt;Model&lt;/code&gt;) instructing the AI to process user input into structured nutritional data—identifying meal components, estimating portion weights, and warning it not to reveal its internal steps. Pass the user input as a prompt (Role: &lt;code&gt;User&lt;/code&gt;) using the expression &lt;code&gt;{{ $json.message }}&lt;/code&gt; along with today&amp;rsquo;s date context: &lt;code&gt;{{ $today.format(&#39;yyyy-MM-dd&#39;) }}&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/using-gemini-ai-via-api.png&#34; alt=&#34;AI Processing Configuration&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;To store the parsed responses, add a Google Sheets node configured to &amp;ldquo;Append&amp;rdquo; rows, and set it to &lt;strong&gt;Map Each Column Manually&lt;/strong&gt;. You can map fields originating from the trigger and model response:\n- &lt;strong&gt;User_ID&lt;/strong&gt;: &lt;code&gt;{{ $json.chat_id }}&lt;/code&gt;\n- &lt;strong&gt;Date&lt;/strong&gt;: Extract and format the date directly with &lt;code&gt;{{ DateTime.fromSeconds($json.date).format(&#39;yyyy-LL-d&#39;) }}&lt;/code&gt;.\n- Dietary metrics like &lt;strong&gt;Meal_description&lt;/strong&gt;, &lt;strong&gt;Calories&lt;/strong&gt;, &lt;strong&gt;Proteins&lt;/strong&gt;, &lt;strong&gt;Carbs&lt;/strong&gt;, and &lt;strong&gt;Fats&lt;/strong&gt; can be defined automatically by mapping them from structured outputs generated by the AI model.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/append-to-excel-sheet.png&#34; alt=&#34;Append to Google Sheets&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is all sorts of existing nodes (in &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;, a &amp;ldquo;node&amp;rdquo; is basically a building block or a step in your automation workflow—it can be a trigger that starts the flow, an action that connects to a specific app&amp;rsquo;s API like sending a message, or a logic step that filters or processes data) that we can use. For this use case, reusing the &lt;a href=&#34;https://developers.google.com/sheets/api/guides/concepts&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google sheet&lt;/a&gt; node from official requires getting API keys, but that is well documented by &lt;a href=&#34;https://docs.n8n.io/integrations/builtin/credentials/google/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and &lt;a href=&#34;https://developers.google.com/workspace/guides/create-credentials&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;Finally, configure another Telegram node to send a reply back to the user. Set the Resource to &lt;code&gt;Message&lt;/code&gt; and Operation to &lt;code&gt;Send Message&lt;/code&gt;. For the &lt;strong&gt;Chat ID&lt;/strong&gt;, retrieve it from the initial trigger using &lt;code&gt;{{ $(&#39;Telegram Trigger&#39;).item.json.message.chat.id }}&lt;/code&gt;. Include the formatted Text from the AI calculation &lt;code&gt;{{ $json.message }}&lt;/code&gt;. Under Additional Fields, be sure to set &lt;strong&gt;Parse Mode&lt;/strong&gt; to &lt;code&gt;MarkdownV2&lt;/code&gt; so any formatting from the AI looks correct, and toggle off &amp;ldquo;Append n8n Attribution&amp;rdquo; for a cleaner message.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/send-telegram-msg-back.png&#34; alt=&#34;Send Telegram Summary Message&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For the initial setup, because it&amp;rsquo;s new, I was experimenting and took 4-5 hours, because it was interesting; but been using that for maintaining calories now; it&amp;rsquo;s great.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;AI is more prevalent these days, instead of building these automation no code solutions via these tools, users directly build with AI, there are pros and cons.. it&amp;rsquo;s already deployed on the server (the automation tool, one time setup), can deploy any number of times.. limit depends on the server limits, I&amp;rsquo;m okay.. but people need to know of how to debug in case of error, I mean there is AI for that too; if using &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;/automation.. everything is contained within this little confinement work area.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll be trying out other automation experiments, this is serving me well, recently I gained a lot of weight, though not overweight.. I still look fit from an outside view; but a little fat in stomach and back.. I started eating snacks every night and lots of oats (used to prepare the previous night with milk and sugar.. keep it in freezer).. the next morning eating it, I drove this diet for some time, it was good for my morning routine (not going to office on an empty stomach), but the problem was I gained like 5 kgs, I have never been this much weight in my entire life! but I&amp;rsquo;m 6ft.. so the weight is kinda explainable.&lt;/p&gt;\n&lt;p&gt;Me when 76kgs, ideal for my height, less fat content ratio in my body; this is before I got muscle buildup.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/me-when-76.jpg&#34; alt=&#34;image when I was 76kgs&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;but in terms of physique, , I got visibly more muscle development..after above image, idk.. started to go to gym again, let&amp;rsquo;s see, till now I&amp;rsquo;ve been continuing to do it every day except Sunday at home. But I&amp;rsquo;ll track the progress.&lt;/p&gt;\n&lt;p&gt;Updating this part as of March 2026, it&amp;rsquo;s happening again, I feel out of place, everyone around here is behind.. so behind .. than I expect them to be in, when I see, I see a fine line between things and it&amp;rsquo;s like I can slip in between them.. and go past beyond. I see it like this, a highway road that is busy with vehicles going left and right, but I see a path in slow motion that I can slip in between and move to the other side, I see what others are not seeing, IDK how long I will be blessed with this gift or curse that I have, can&amp;rsquo;t share this with anyone, I exposed this lil bit to sq#3, ~Future me is this happening again? Hope you landed on things, learned from past.&lt;/p&gt;</description></item><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item></channel></rss>"}, "783c20c06c53f2c6a43ce43580382db7e1e17e45": {"t": 1785770739.6733224, "n": 165940, "h": "f6cd4c9311bf63ca", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Tue, 03 Mar 2026 05:24:25 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>&#39;AI Skills&#39;</title><link>//bluepie.in/2026/03/mar-2026/</link><pubDate>Tue, 03 Mar 2026 05:24:25 +0000</pubDate><guid>//bluepie.in/2026/03/mar-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Anthropic came and published their skills documentation on what they are roughly 2 months ago, but I suspect they and other AI companies were using them internally even before they published. The idea of people seeing them as a new thing and being astonished is abysmal..because we were already using it..but without the naming convention and the optimised context limiting. The concept is simple: you create a folder named skill you want, then inside it, the main file is called skill.md. This takes less context when ai reads it, as it just reads the frontmatter alone; you write detailed instructions below the frontmatter with different scripts and whatever that is necessary to finish that task/skill, you also writter a condition upon which the ai will trigger. The AI then keeps all these details and uses it ad hoc. The problem it solves is that this uses way less context than mcp or others and only triggers when necessary.&lt;/p&gt;\n&lt;h2 id=&#34;example-plantuml-generator&#34;&gt;Example: PlantUML Generator&lt;/h2&gt;\n&lt;p&gt;So the idea is simple; as you can infer from above, let&amp;rsquo;s go with an example. I wanted to create an architecture diagram skill in office. ..in office we use he PlantUML to create sequence diagrams.&lt;/p&gt;\n&lt;p&gt;The problem is you have to ask the AI to generate it, and then generate the image and maintain the image somewhere. The problem with this is it takes time and its a manual process. How can we use skills to automate this? We can write instructions to ask the AI chat / agent to be like, if a user asks x, then do the process and update the diagram. We can add these instructions in the skill.md file, keep the name and descriptions to work with the architecture diagram.&lt;/p&gt;\n&lt;p&gt;You can write scripts and then use them to generate things; the AI will execute them based on the instructions. The next is dependencies, if any. Since PlantUML is an open-source project, so we can download the jar file and put that in the skills folder. since its a java project..it&amp;rsquo;s platform independent, so no need to worry about different binaries&lt;/p&gt;\n&lt;p&gt;So everything can be packaged into a single skill.&lt;/p&gt;\n&lt;p&gt;you can see the skills folder here: &lt;a href=&#34;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;embedding-code-in-images&#34;&gt;Embedding Code in Images&lt;/h2&gt;\n&lt;p&gt;On top of the skill, I was thinking about for a optimization. In college, I conducted a Capture the Flag; the event was like Google CTF and other CTF events from that time. I was inspired by them, Anyway. one of the competitions was to find the hidden details from the image. Inside an image, you can add a data to it, it can be any data. So I was thinking, why can&amp;rsquo;t we use the same thing here? So I asked ai to write a script to embed code into the images. So the PlantUML diagram is stored inside the images, the images are the output of the code. It&amp;rsquo;s like a chicken and egg problem once you delete the code file from the repo.&lt;/p&gt;\n&lt;p&gt;We want crud options on this; the update image, edit image, and create image are just a set of instructions that the AI can read and execute.. in my case it will be Python scripts, when user asks to edit, it will extract the code from the image and create a temporary file we can work on, and when we ask to store it, it saves the source code, again inside the image, deleting the code file.&lt;/p&gt;\n&lt;h2 id=&#34;other-skills&#34;&gt;Other Skills&lt;/h2&gt;\n&lt;p&gt;There are other skills that I use here, it keeps changing overtime.&lt;/p&gt;\n&lt;p&gt;The main one I wanted to share is the PlantUML generator; it has all the features that I need to be adding to demonstrate the features of the skill.&lt;/p&gt;\n&lt;h2 id=&#34;sub-agents-lifecycle-hooks&#34;&gt;Sub-agents &amp;amp; Lifecycle Hooks&lt;/h2&gt;\n&lt;p&gt;At the end of Feb 2026, the Claude team updated the skillset to have custom features, like sub-agents. What it is, is that the sub-agent is being called by the main agent when doing a certain task.. so it will fix the context problem. it&amp;rsquo;s one of the major updates to skills; it&amp;rsquo;s the major one that makes a significant difference, because it won&amp;rsquo;t clutter the main context; the main agent is not interfered with.&lt;/p&gt;\n&lt;p&gt;It also has access to the lifecycle events, Lifecycle hooks (on_start, on_success, on_error, PreToolUse, etc.).. but i haven&amp;rsquo;t adopted them yet; but this will help me with the cryo vault project: &lt;a href=&#34;https://github.com/aghontpi/cryo-vault&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/cryo-vault&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m having to juggle a lot of things in my life currently; finding focus time to think on what is essential or next is hard with this attention-seeking world; socializing; maintaining health and ad hoc new plans; argh! I started to have a new habit; hopefully, when I am consistent on this.. I&amp;rsquo;ll post on this later!.. future me..! what is happening? hope you are good!&lt;/p&gt;</description></item><item><title>Selfhosting an Automation tool - N8N</title><link>//bluepie.in/2026/02/feb-2026-2/</link><pubDate>Sat, 28 Feb 2026 15:17:41 +0000</pubDate><guid>//bluepie.in/2026/02/feb-2026-2/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;In the previous post, I was talking about &lt;a href=&#34;https://claude.com/skills&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;claude&lt;/a&gt; skills. This is about automation, no code/ minimal solutions. At this point, it&amp;rsquo;s been over 1.5 to 2 years since &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and automation; nowadays it&amp;rsquo;s easy to just build an app/ ask AI to build the app instead of creating the automation, it&amp;rsquo;s easier that way, but regardless, let&amp;rsquo;s see how the future goes; I&amp;rsquo;m already seeing the trend of creating custom apps for each one&amp;rsquo;s personal preference.&lt;/p&gt;\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;Instead of creating projects for each use and hosting it for every use case, I thought of using automation tools and solving those problems; for ex, one such problem is creating a diet maintainer; I ranted in a previous post that I gained a couple of kgs in a short while, but I&amp;rsquo;m trying to reduce it; so I have to keep track of the amount of calories, etc., but I want this to be an Excel sheet for tracking, yes there are apps for that; but I wanted a simple chat interface where I can type the food I ate and when I ate it, and it should approximate the calories, and when I ask how much is left, it should give back the amount of food that I can eat in the remaining part of the day/ from that point onwards.&lt;/p&gt;\n&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;\n&lt;p&gt;I choose n8n. The main reason I prefer it is the immense community support it has, it&amp;rsquo;s incredibly popular, which means if I run into a wall, someone else has probably already solved it and posted about it. Plus, being able to tinker with it under the hood while self-hosting is a massive bonus compared to locked-in cloud alternatives.&lt;/p&gt;\n&lt;p&gt;Speaking of alternatives, there are quite a few out there:\n- &lt;a href=&#34;https://www.make.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Make (formerly Integromat)&lt;/a&gt;: Great visual builder,\n- &lt;a href=&#34;https://zapier.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Zapier&lt;/a&gt;: The biggest name out there with the most integrations,\n- &lt;a href=&#34;https://pipedream.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Pipedream&lt;/a&gt;: Fantastic for developers who want to write code snippets, but maybe overkill if you just want pure drag-and-drop.\n- &lt;a href=&#34;https://www.activepieces.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Activepieces&lt;/a&gt;: is similar to n8n&lt;/p&gt;\n&lt;h2 id=&#34;self-hosting&#34;&gt;Self hosting&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Here is the official n8n documentation guide&lt;/a&gt;, I started to see the official docs of n8n. One thing I got surprised by is that they have incorporated AI, and that AI is surprisingly good at providing the right instructions, because when you host with different servers, server config differs for each one like &lt;a href=&#34;https://caddyserver.com/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;caddy&lt;/a&gt; / &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;/ etc., and for automation to work, it has to be able to receive webhooks, and alongside things like if it&amp;rsquo;s behind a proxy; it must be configured to tell that you are behind a proxy, and based on that it can handle the incoming request, etc.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll save you the trouble and give the config here; basically I was able to set up the Nginx server pretty easily using the AI bot that they have incorporated in their site. Below is all the config I needed to be able to run this from &lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; and &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;:&lt;/p&gt;\n&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I like to run things with &lt;a href=&#34;https://docs.podman.io/en/latest/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;podman&lt;/a&gt;/&lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; as it removes the dependency from my cloud machine.&lt;/p&gt;\n&lt;p&gt;Obviously I have to configure my domain name to &lt;a href=&#34;https://www.godaddy.com/help&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;godaddy&lt;/a&gt;/&lt;a href=&#34;https://developers.cloudflare.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt; and deploy SSL certificates; I&amp;rsquo;ve covered that plenty in my other posts:&lt;/p&gt;\n&lt;p&gt;docker config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;podman run -d --restart unless-stopped \\\n--name n8n \\\n-p 5678:5678 \\\n-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \\\n-e N8N_RUNNERS_ENABLED=true \\\n-e WEBHOOK_URL=https://&amp;lt;server-name&amp;gt;/ \\\n-e N8N_PROXY_HOPS=1 \\\n-e N8N_HOST=&amp;lt;server-name&amp;gt;\n-e N8N_PORT=5678 \\\n-e N8N_PROTOCOL=https \\\n-v /mnt/podman-data/n8n_data:/home/node/.n8n:Z \\\ndocker.n8n.io/n8nio/n8n\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;nginx config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;server {\nlisten 80;\nserver_name &amp;lt;server-name&amp;gt;\nlocation / {\nproxy_pass http://127.0.0.1:5678;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_set_header X-Forwarded-Port $server_port;\nchunked_transfer_encoding off;\nproxy_buffering off;\nproxy_cache off;\n}\nlocation /webhook/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\nlocation /webhook-test/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-how-to-configure&#34;&gt;Configuring - How to configure&lt;/h2&gt;\n&lt;p&gt;Modifying a telegram bot to track the calory intake of the day based on estimates from me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/overview.png&#34; alt=&#34;Overview&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is initial learning time to get used to what each option is, but when you get the hang of it, it&amp;rsquo;s pretty easy, since I&amp;rsquo;ve done drag and drop editors before; I caught up easily in approx 5-10ish mins.&lt;/p&gt;\n&lt;p&gt;Create a &lt;a href=&#34;https://core.telegram.org/bots/api&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;telegram&lt;/a&gt; node and select the appropriate Telegram account credential. Set the &lt;strong&gt;Trigger On&lt;/strong&gt; parameter to &lt;code&gt;Message&lt;/code&gt; so it triggers on new incoming messages of any kind (text, photo, sticker, etc.). Note that due to Telegram API limitations, you can only use one Telegram trigger per bot. Also ensure there is validation to only process messages from your known user ID, otherwise the bot will refuse to respond.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/telegram-trigger-receive.png&#34; alt=&#34;Telegram Trigger Settings&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Add an &lt;strong&gt;Analyze Text Message&lt;/strong&gt; node and connect it to your Google Gemini (or equivalent API) configuration. Set the Resource to &lt;code&gt;Text&lt;/code&gt; and Operation to &lt;code&gt;Message a Model&lt;/code&gt;, using the &lt;code&gt;models/gemini-2.5-flash&lt;/code&gt; model so its fast and reliable. models cheaper than 2.5-flash are not reliable based on testing. Within the node, establish a system prompt (Role: &lt;code&gt;Model&lt;/code&gt;) instructing the AI to process user input into structured nutritional data—identifying meal components, estimating portion weights, and warning it not to reveal its internal steps. Pass the user input as a prompt (Role: &lt;code&gt;User&lt;/code&gt;) using the expression &lt;code&gt;{{ $json.message }}&lt;/code&gt; along with today&amp;rsquo;s date context: &lt;code&gt;{{ $today.format(&#39;yyyy-MM-dd&#39;) }}&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/using-gemini-ai-via-api.png&#34; alt=&#34;AI Processing Configuration&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;To store the parsed responses, add a Google Sheets node configured to &amp;ldquo;Append&amp;rdquo; rows, and set it to &lt;strong&gt;Map Each Column Manually&lt;/strong&gt;. You can map fields originating from the trigger and model response:\n- &lt;strong&gt;User_ID&lt;/strong&gt;: &lt;code&gt;{{ $json.chat_id }}&lt;/code&gt;\n- &lt;strong&gt;Date&lt;/strong&gt;: Extract and format the date directly with &lt;code&gt;{{ DateTime.fromSeconds($json.date).format(&#39;yyyy-LL-d&#39;) }}&lt;/code&gt;.\n- Dietary metrics like &lt;strong&gt;Meal_description&lt;/strong&gt;, &lt;strong&gt;Calories&lt;/strong&gt;, &lt;strong&gt;Proteins&lt;/strong&gt;, &lt;strong&gt;Carbs&lt;/strong&gt;, and &lt;strong&gt;Fats&lt;/strong&gt; can be defined automatically by mapping them from structured outputs generated by the AI model.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/append-to-excel-sheet.png&#34; alt=&#34;Append to Google Sheets&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is all sorts of existing nodes (in &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;, a &amp;ldquo;node&amp;rdquo; is basically a building block or a step in your automation workflow—it can be a trigger that starts the flow, an action that connects to a specific app&amp;rsquo;s API like sending a message, or a logic step that filters or processes data) that we can use. For this use case, reusing the &lt;a href=&#34;https://developers.google.com/sheets/api/guides/concepts&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google sheet&lt;/a&gt; node from official requires getting API keys, but that is well documented by &lt;a href=&#34;https://docs.n8n.io/integrations/builtin/credentials/google/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and &lt;a href=&#34;https://developers.google.com/workspace/guides/create-credentials&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;Finally, configure another Telegram node to send a reply back to the user. Set the Resource to &lt;code&gt;Message&lt;/code&gt; and Operation to &lt;code&gt;Send Message&lt;/code&gt;. For the &lt;strong&gt;Chat ID&lt;/strong&gt;, retrieve it from the initial trigger using &lt;code&gt;{{ $(&#39;Telegram Trigger&#39;).item.json.message.chat.id }}&lt;/code&gt;. Include the formatted Text from the AI calculation &lt;code&gt;{{ $json.message }}&lt;/code&gt;. Under Additional Fields, be sure to set &lt;strong&gt;Parse Mode&lt;/strong&gt; to &lt;code&gt;MarkdownV2&lt;/code&gt; so any formatting from the AI looks correct, and toggle off &amp;ldquo;Append n8n Attribution&amp;rdquo; for a cleaner message.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/send-telegram-msg-back.png&#34; alt=&#34;Send Telegram Summary Message&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For the initial setup, because it&amp;rsquo;s new, I was experimenting and took 4-5 hours, because it was interesting; but been using that for maintaining calories now; it&amp;rsquo;s great.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;AI is more prevalent these days, instead of building these automation no code solutions via these tools, users directly build with AI, there are pros and cons.. it&amp;rsquo;s already deployed on the server (the automation tool, one time setup), can deploy any number of times.. limit depends on the server limits, I&amp;rsquo;m okay.. but people need to know of how to debug in case of error, I mean there is AI for that too; if using &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;/automation.. everything is contained within this little confinement work area.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll be trying out other automation experiments, this is serving me well, recently I gained a lot of weight, though not overweight.. I still look fit from an outside view; but a little fat in stomach and back.. I started eating snacks every night and lots of oats (used to prepare the previous night with milk and sugar.. keep it in freezer).. the next morning eating it, I drove this diet for some time, it was good for my morning routine (not going to office on an empty stomach), but the problem was I gained like 5 kgs, I have never been this much weight in my entire life! but I&amp;rsquo;m 6ft.. so the weight is kinda explainable.&lt;/p&gt;\n&lt;p&gt;Me when 76kgs, ideal for my height, less fat content ratio in my body; this is before I got muscle buildup.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/me-when-76.jpg&#34; alt=&#34;image when I was 76kgs&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;but in terms of physique, , I got visibly more muscle development..after above image, idk.. started to go to gym again, let&amp;rsquo;s see, till now I&amp;rsquo;ve been continuing to do it every day except Sunday at home. But I&amp;rsquo;ll track the progress.&lt;/p&gt;\n&lt;p&gt;Updating this part as of March 2026, it&amp;rsquo;s happening again, I feel out of place, everyone around here is behind.. so behind .. than I expect them to be in, when I see, I see a fine line between things and it&amp;rsquo;s like I can slip in between them.. and go past beyond. I see it like this, a highway road that is busy with vehicles going left and right, but I see a path in slow motion that I can slip in between and move to the other side, I see what others are not seeing, IDK how long I will be blessed with this gift or curse that I have, can&amp;rsquo;t share this with anyone, I exposed this lil bit to sq#3, ~Future me is this happening again? Hope you landed on things, learned from past.&lt;/p&gt;</description></item><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item></channel></rss>"}, "af0de5fe2df0edc10bf3b6cdb8824b032d65ba94": {"t": 1785770742.1247573, "n": 165756, "h": "ff54784a052c38bf", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Tue, 03 Mar 2026 05:24:25 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>&#39;AI Skills&#39;</title><link>//bluepie.in/2026/03/mar-2026/</link><pubDate>Tue, 03 Mar 2026 05:24:25 +0000</pubDate><guid>//bluepie.in/2026/03/mar-2026/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;Anthropic came and published their skills documentation and what they are roughly 2 months ago, but I suspect they and other AI companies were using them internally even before they published. The idea of people seeing them as a new thing and being astonished is abysmal..because we were already using it..but without the naming convention and the context limiting. The concept is simple: you create a folder named the skill, then inside it, the main file is called skill.md. This takes less context; you tell the AI to scan all skill folders for skills that it can use ad hoc. The problem it solves with MCP is that this uses way less context than others and only triggers when necessary.&lt;/p&gt;\n&lt;h2 id=&#34;example-plantuml-generator&#34;&gt;Example: PlantUML Generator&lt;/h2&gt;\n&lt;p&gt;So the idea is simple; as you can infer from what I was saying, let&amp;rsquo;s go with an example. I wanted to create an architecture diagram in the office and have to update them, ..in office we are using the PlantUML to create sequence diagrams.&lt;/p&gt;\n&lt;p&gt;The problem is you have to ask the AI to generate it, and then generate the image and maintain the image somewhere. The problem with this is it takes time and is a manual process. How can we use skills to automate this? We can write instructions to ask the AI chat / agent to be like, if a user asks x, then do the process and update the diagram. We can add these instructions in the skill.md file, keep the name and descriptions to work with the architecture diagram.&lt;/p&gt;\n&lt;p&gt;You can write scripts and then use them to generate things; the AI will execute them based on the instructions. The next is dependencies, if any. Since PlantUML is an open-source project, so we can download the jar file and put that in the skills folder. It&amp;rsquo;s byte code and it&amp;rsquo;s platform independent.&lt;/p&gt;\n&lt;p&gt;So everything can be packaged into a single skill.&lt;/p&gt;\n&lt;p&gt;you can see the skills folder here: &lt;a href=&#34;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/dot-configs-ai/tree/main/skills/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;embedding-code-in-images&#34;&gt;Embedding Code in Images&lt;/h2&gt;\n&lt;p&gt;On top of the skill, I was thinking about for a optimization. In college, I conducted a Capture the Flag; the event was like Google CTF and other CTF events from that time. I was inspired by them, Anyway. one of the competitions was to find the hidden details from the image. Inside an image, you can add a data to it, it can be any data. So I was thinking, why can&amp;rsquo;t we use the same thing here? So I asked ai to write a script to embed code into the images. So the PlantUML diagram is stored inside the images, the images are the output of the code. It&amp;rsquo;s like a chicken and egg problem once you delete the code file from the repo.&lt;/p&gt;\n&lt;p&gt;We want crud options on this; the update image, edit image, and create image commands are just a set of instructions that the AI can read and execute. Python scripts, when wanted to edit / asked to edit, it will extract the code from the image and create a temporary file we can work on, and when we ask to store it, it saves it again inside the image, deleting the code file again.&lt;/p&gt;\n&lt;h2 id=&#34;other-skills&#34;&gt;Other Skills&lt;/h2&gt;\n&lt;p&gt;There are other skills that I use here, one skill that I use to review code. It has custom code review rules.. it keeps changing overtime.&lt;/p&gt;\n&lt;p&gt;The main one I wanted to share is the PlantUML generator; it has all the features that I need to be adding to demonstrate the features of the skill.&lt;/p&gt;\n&lt;h2 id=&#34;sub-agents-lifecycle-hooks&#34;&gt;Sub-agents &amp;amp; Lifecycle Hooks&lt;/h2&gt;\n&lt;p&gt;At the end of Feb 2026, the Claude team updated the skillset to have custom features, like sub-agents. What it is, is that the sub-agent is being called by the main agent when doing a certain task.. so it will fix the context problem. it&amp;rsquo;s one of the major updates to skills; it&amp;rsquo;s the major one that makes a significant difference, because it won&amp;rsquo;t clutter the main context; the main agent is not interfered with.&lt;/p&gt;\n&lt;p&gt;It also has access to the lifecycle events, Lifecycle hooks (on_start, on_success, on_error, PreToolUse, etc.).. but i haven&amp;rsquo;t adopted them yet; but this will help me with the cryo vault project: &lt;a href=&#34;https://github.com/aghontpi/cryo-vault&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/cryo-vault&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m having to juggle a lot of things in my life currently; finding focus time to think on what is essential or next is hard with this attention-seeking world; socializing; maintaining health and ad hoc new plans; argh! I started to have a new habit; hopefully, when I am consistent on this.. I&amp;rsquo;ll post on this later!.. future me..! what is happening? hope you are good!&lt;/p&gt;</description></item><item><title>Selfhosting an Automation tool - N8N</title><link>//bluepie.in/2026/02/feb-2026-2/</link><pubDate>Sat, 28 Feb 2026 15:17:41 +0000</pubDate><guid>//bluepie.in/2026/02/feb-2026-2/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;In the previous post, I was talking about &lt;a href=&#34;https://claude.com/skills&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;claude&lt;/a&gt; skills. This is about automation, no code/ minimal solutions. At this point, it&amp;rsquo;s been over 1.5 to 2 years since &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and automation; nowadays it&amp;rsquo;s easy to just build an app/ ask AI to build the app instead of creating the automation, it&amp;rsquo;s easier that way, but regardless, let&amp;rsquo;s see how the future goes; I&amp;rsquo;m already seeing the trend of creating custom apps for each one&amp;rsquo;s personal preference.&lt;/p&gt;\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;Instead of creating projects for each use and hosting it for every use case, I thought of using automation tools and solving those problems; for ex, one such problem is creating a diet maintainer; I ranted in a previous post that I gained a couple of kgs in a short while, but I&amp;rsquo;m trying to reduce it; so I have to keep track of the amount of calories, etc., but I want this to be an Excel sheet for tracking, yes there are apps for that; but I wanted a simple chat interface where I can type the food I ate and when I ate it, and it should approximate the calories, and when I ask how much is left, it should give back the amount of food that I can eat in the remaining part of the day/ from that point onwards.&lt;/p&gt;\n&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;\n&lt;p&gt;I choose n8n. The main reason I prefer it is the immense community support it has, it&amp;rsquo;s incredibly popular, which means if I run into a wall, someone else has probably already solved it and posted about it. Plus, being able to tinker with it under the hood while self-hosting is a massive bonus compared to locked-in cloud alternatives.&lt;/p&gt;\n&lt;p&gt;Speaking of alternatives, there are quite a few out there:\n- &lt;a href=&#34;https://www.make.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Make (formerly Integromat)&lt;/a&gt;: Great visual builder,\n- &lt;a href=&#34;https://zapier.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Zapier&lt;/a&gt;: The biggest name out there with the most integrations,\n- &lt;a href=&#34;https://pipedream.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Pipedream&lt;/a&gt;: Fantastic for developers who want to write code snippets, but maybe overkill if you just want pure drag-and-drop.\n- &lt;a href=&#34;https://www.activepieces.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Activepieces&lt;/a&gt;: is similar to n8n&lt;/p&gt;\n&lt;h2 id=&#34;self-hosting&#34;&gt;Self hosting&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Here is the official n8n documentation guide&lt;/a&gt;, I started to see the official docs of n8n. One thing I got surprised by is that they have incorporated AI, and that AI is surprisingly good at providing the right instructions, because when you host with different servers, server config differs for each one like &lt;a href=&#34;https://caddyserver.com/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;caddy&lt;/a&gt; / &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;/ etc., and for automation to work, it has to be able to receive webhooks, and alongside things like if it&amp;rsquo;s behind a proxy; it must be configured to tell that you are behind a proxy, and based on that it can handle the incoming request, etc.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll save you the trouble and give the config here; basically I was able to set up the Nginx server pretty easily using the AI bot that they have incorporated in their site. Below is all the config I needed to be able to run this from &lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; and &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;:&lt;/p&gt;\n&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I like to run things with &lt;a href=&#34;https://docs.podman.io/en/latest/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;podman&lt;/a&gt;/&lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; as it removes the dependency from my cloud machine.&lt;/p&gt;\n&lt;p&gt;Obviously I have to configure my domain name to &lt;a href=&#34;https://www.godaddy.com/help&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;godaddy&lt;/a&gt;/&lt;a href=&#34;https://developers.cloudflare.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt; and deploy SSL certificates; I&amp;rsquo;ve covered that plenty in my other posts:&lt;/p&gt;\n&lt;p&gt;docker config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;podman run -d --restart unless-stopped \\\n--name n8n \\\n-p 5678:5678 \\\n-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \\\n-e N8N_RUNNERS_ENABLED=true \\\n-e WEBHOOK_URL=https://&amp;lt;server-name&amp;gt;/ \\\n-e N8N_PROXY_HOPS=1 \\\n-e N8N_HOST=&amp;lt;server-name&amp;gt;\n-e N8N_PORT=5678 \\\n-e N8N_PROTOCOL=https \\\n-v /mnt/podman-data/n8n_data:/home/node/.n8n:Z \\\ndocker.n8n.io/n8nio/n8n\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;nginx config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;server {\nlisten 80;\nserver_name &amp;lt;server-name&amp;gt;\nlocation / {\nproxy_pass http://127.0.0.1:5678;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_set_header X-Forwarded-Port $server_port;\nchunked_transfer_encoding off;\nproxy_buffering off;\nproxy_cache off;\n}\nlocation /webhook/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\nlocation /webhook-test/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-how-to-configure&#34;&gt;Configuring - How to configure&lt;/h2&gt;\n&lt;p&gt;Modifying a telegram bot to track the calory intake of the day based on estimates from me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/overview.png&#34; alt=&#34;Overview&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is initial learning time to get used to what each option is, but when you get the hang of it, it&amp;rsquo;s pretty easy, since I&amp;rsquo;ve done drag and drop editors before; I caught up easily in approx 5-10ish mins.&lt;/p&gt;\n&lt;p&gt;Create a &lt;a href=&#34;https://core.telegram.org/bots/api&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;telegram&lt;/a&gt; node and select the appropriate Telegram account credential. Set the &lt;strong&gt;Trigger On&lt;/strong&gt; parameter to &lt;code&gt;Message&lt;/code&gt; so it triggers on new incoming messages of any kind (text, photo, sticker, etc.). Note that due to Telegram API limitations, you can only use one Telegram trigger per bot. Also ensure there is validation to only process messages from your known user ID, otherwise the bot will refuse to respond.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/telegram-trigger-receive.png&#34; alt=&#34;Telegram Trigger Settings&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Add an &lt;strong&gt;Analyze Text Message&lt;/strong&gt; node and connect it to your Google Gemini (or equivalent API) configuration. Set the Resource to &lt;code&gt;Text&lt;/code&gt; and Operation to &lt;code&gt;Message a Model&lt;/code&gt;, using the &lt;code&gt;models/gemini-2.5-flash&lt;/code&gt; model so its fast and reliable. models cheaper than 2.5-flash are not reliable based on testing. Within the node, establish a system prompt (Role: &lt;code&gt;Model&lt;/code&gt;) instructing the AI to process user input into structured nutritional data—identifying meal components, estimating portion weights, and warning it not to reveal its internal steps. Pass the user input as a prompt (Role: &lt;code&gt;User&lt;/code&gt;) using the expression &lt;code&gt;{{ $json.message }}&lt;/code&gt; along with today&amp;rsquo;s date context: &lt;code&gt;{{ $today.format(&#39;yyyy-MM-dd&#39;) }}&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/using-gemini-ai-via-api.png&#34; alt=&#34;AI Processing Configuration&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;To store the parsed responses, add a Google Sheets node configured to &amp;ldquo;Append&amp;rdquo; rows, and set it to &lt;strong&gt;Map Each Column Manually&lt;/strong&gt;. You can map fields originating from the trigger and model response:\n- &lt;strong&gt;User_ID&lt;/strong&gt;: &lt;code&gt;{{ $json.chat_id }}&lt;/code&gt;\n- &lt;strong&gt;Date&lt;/strong&gt;: Extract and format the date directly with &lt;code&gt;{{ DateTime.fromSeconds($json.date).format(&#39;yyyy-LL-d&#39;) }}&lt;/code&gt;.\n- Dietary metrics like &lt;strong&gt;Meal_description&lt;/strong&gt;, &lt;strong&gt;Calories&lt;/strong&gt;, &lt;strong&gt;Proteins&lt;/strong&gt;, &lt;strong&gt;Carbs&lt;/strong&gt;, and &lt;strong&gt;Fats&lt;/strong&gt; can be defined automatically by mapping them from structured outputs generated by the AI model.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/append-to-excel-sheet.png&#34; alt=&#34;Append to Google Sheets&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is all sorts of existing nodes (in &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;, a &amp;ldquo;node&amp;rdquo; is basically a building block or a step in your automation workflow—it can be a trigger that starts the flow, an action that connects to a specific app&amp;rsquo;s API like sending a message, or a logic step that filters or processes data) that we can use. For this use case, reusing the &lt;a href=&#34;https://developers.google.com/sheets/api/guides/concepts&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google sheet&lt;/a&gt; node from official requires getting API keys, but that is well documented by &lt;a href=&#34;https://docs.n8n.io/integrations/builtin/credentials/google/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and &lt;a href=&#34;https://developers.google.com/workspace/guides/create-credentials&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;Finally, configure another Telegram node to send a reply back to the user. Set the Resource to &lt;code&gt;Message&lt;/code&gt; and Operation to &lt;code&gt;Send Message&lt;/code&gt;. For the &lt;strong&gt;Chat ID&lt;/strong&gt;, retrieve it from the initial trigger using &lt;code&gt;{{ $(&#39;Telegram Trigger&#39;).item.json.message.chat.id }}&lt;/code&gt;. Include the formatted Text from the AI calculation &lt;code&gt;{{ $json.message }}&lt;/code&gt;. Under Additional Fields, be sure to set &lt;strong&gt;Parse Mode&lt;/strong&gt; to &lt;code&gt;MarkdownV2&lt;/code&gt; so any formatting from the AI looks correct, and toggle off &amp;ldquo;Append n8n Attribution&amp;rdquo; for a cleaner message.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/send-telegram-msg-back.png&#34; alt=&#34;Send Telegram Summary Message&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For the initial setup, because it&amp;rsquo;s new, I was experimenting and took 4-5 hours, because it was interesting; but been using that for maintaining calories now; it&amp;rsquo;s great.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;AI is more prevalent these days, instead of building these automation no code solutions via these tools, users directly build with AI, there are pros and cons.. it&amp;rsquo;s already deployed on the server (the automation tool, one time setup), can deploy any number of times.. limit depends on the server limits, I&amp;rsquo;m okay.. but people need to know of how to debug in case of error, I mean there is AI for that too; if using &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;/automation.. everything is contained within this little confinement work area.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll be trying out other automation experiments, this is serving me well, recently I gained a lot of weight, though not overweight.. I still look fit from an outside view; but a little fat in stomach and back.. I started eating snacks every night and lots of oats (used to prepare the previous night with milk and sugar.. keep it in freezer).. the next morning eating it, I drove this diet for some time, it was good for my morning routine (not going to office on an empty stomach), but the problem was I gained like 5 kgs, I have never been this much weight in my entire life! but I&amp;rsquo;m 6ft.. so the weight is kinda explainable.&lt;/p&gt;\n&lt;p&gt;Me when 76kgs, ideal for my height, less fat content ratio in my body; this is before I got muscle buildup.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/me-when-76.jpg&#34; alt=&#34;image when I was 76kgs&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;but in terms of physique, , I got visibly more muscle development..after above image, idk.. started to go to gym again, let&amp;rsquo;s see, till now I&amp;rsquo;ve been continuing to do it every day except Sunday at home. But I&amp;rsquo;ll track the progress.&lt;/p&gt;\n&lt;p&gt;Updating this part as of March 2026, it&amp;rsquo;s happening again, I feel out of place, everyone around here is behind.. so behind .. than I expect them to be in, when I see, I see a fine line between things and it&amp;rsquo;s like I can slip in between them.. and go past beyond. I see it like this, a highway road that is busy with vehicles going left and right, but I see a path in slow motion that I can slip in between and move to the other side, I see what others are not seeing, IDK how long I will be blessed with this gift or curse that I have, can&amp;rsquo;t share this with anyone, I exposed this lil bit to sq#3, ~Future me is this happening again? Hope you landed on things, learned from past.&lt;/p&gt;</description></item><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item></channel></rss>"}, "0914e72409b801b3db36f866a0d9156d40ecbb44": {"t": 1785770744.7372348, "n": 170346, "h": "31190b2dd8e688d4", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sat, 28 Feb 2026 15:17:41 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Selfhosting an Automation tool - N8N</title><link>//bluepie.in/2026/02/feb-2026-2/</link><pubDate>Sat, 28 Feb 2026 15:17:41 +0000</pubDate><guid>//bluepie.in/2026/02/feb-2026-2/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;In the previous post, I was talking about &lt;a href=&#34;https://claude.com/skills&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;claude&lt;/a&gt; skills. This is about automation, no code/ minimal solutions. At this point, it&amp;rsquo;s been over 1.5 to 2 years since &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and automation; nowadays it&amp;rsquo;s easy to just build an app/ ask AI to build the app instead of creating the automation, it&amp;rsquo;s easier that way, but regardless, let&amp;rsquo;s see how the future goes; I&amp;rsquo;m already seeing the trend of creating custom apps for each one&amp;rsquo;s personal preference.&lt;/p&gt;\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;Instead of creating projects for each use and hosting it for every use case, I thought of using automation tools and solving those problems; for ex, one such problem is creating a diet maintainer; I ranted in a previous post that I gained a couple of kgs in a short while, but I&amp;rsquo;m trying to reduce it; so I have to keep track of the amount of calories, etc., but I want this to be an Excel sheet for tracking, yes there are apps for that; but I wanted a simple chat interface where I can type the food I ate and when I ate it, and it should approximate the calories, and when I ask how much is left, it should give back the amount of food that I can eat in the remaining part of the day/ from that point onwards.&lt;/p&gt;\n&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;\n&lt;p&gt;I choose &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;. The main reason I prefer it is the immense community support it has, it&amp;rsquo;s incredibly popular, which means if I run into a wall, someone else has probably already solved it and posted about it. Plus, being able to tinker with it under the hood while self-hosting is a massive bonus compared to locked-in cloud alternatives.&lt;/p&gt;\n&lt;p&gt;Speaking of alternatives, there are quite a few out there:\n- &lt;a href=&#34;https://www.make.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Make (formerly Integromat)&lt;/a&gt;: Great visual builder,\n- &lt;a href=&#34;https://zapier.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Zapier&lt;/a&gt;: The biggest name out there with the most integrations,\n- &lt;a href=&#34;https://pipedream.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Pipedream&lt;/a&gt;: Fantastic for developers who want to write code snippets, but maybe overkill if you just want pure drag-and-drop.\n- &lt;a href=&#34;https://www.activepieces.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Activepieces&lt;/a&gt;: is similar to n8n&lt;/p&gt;\n&lt;h2 id=&#34;self-hosting&#34;&gt;Self hosting&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Here is the official n8n documentation guide&lt;/a&gt;, I started to see the official docs of &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;. One thing I got astonished by is that they have incorporated AI, and that AI is surprisingly good at providing the right instructions, because when you host with different server software, server config differs for each one like &lt;a href=&#34;https://caddyserver.com/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;caddy&lt;/a&gt; / &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;/ etc., and for automation to work, it has to be able to receive webhooks, and alongside things like if it&amp;rsquo;s behind a proxy; it must be configured to tell that you are behind a proxy, and based on that it can handle the incoming request, etc.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll save you the trouble and give the config here; basically I was able to set up the Nginx server pretty easily using the AI bot that they have incorporated in their site. Below is all the config I needed to be able to run this from &lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; and &lt;a href=&#34;https://nginx.org/en/docs/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;nginx&lt;/a&gt;:&lt;/p&gt;\n&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I like to run things with &lt;a href=&#34;https://docs.podman.io/en/latest/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;podman&lt;/a&gt;/&lt;a href=&#34;https://docs.docker.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;docker&lt;/a&gt; as it removes the dependency from my cloud machine.&lt;/p&gt;\n&lt;p&gt;Obviously I have to configure my domain name to &lt;a href=&#34;https://www.godaddy.com/help&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;godaddy&lt;/a&gt;/&lt;a href=&#34;https://developers.cloudflare.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt; and deploy SSL certificates; I&amp;rsquo;ve covered that plenty in my other posts:&lt;/p&gt;\n&lt;p&gt;docker config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;podman run -d --restart unless-stopped \\\n--name n8n \\\n-p 5678:5678 \\\n-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \\\n-e N8N_RUNNERS_ENABLED=true \\\n-e WEBHOOK_URL=https://&amp;lt;server-name&amp;gt;/ \\\n-e N8N_PROXY_HOPS=1 \\\n-e N8N_HOST=&amp;lt;server-name&amp;gt;\n-e N8N_PORT=5678 \\\n-e N8N_PROTOCOL=https \\\n-v /mnt/podman-data/n8n_data:/home/node/.n8n:Z \\\ndocker.n8n.io/n8nio/n8n\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;nginx config:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;server {\nlisten 80;\nserver_name &amp;lt;server-name&amp;gt;\nlocation / {\nproxy_pass http://127.0.0.1:5678;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_set_header X-Forwarded-Port $server_port;\nchunked_transfer_encoding off;\nproxy_buffering off;\nproxy_cache off;\n}\nlocation /webhook/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\nlocation /webhook-test/ {\nproxy_pass http://127.0.0.1:5678;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-how-to-configure&#34;&gt;Configuring - How to configure&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/overview.png&#34; alt=&#34;Overview&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is initial learning time to get used to what each option is, but when you get the hang of it, it&amp;rsquo;s pretty easy, since I&amp;rsquo;ve done drag and drop editors before; I caught up easily in approx 5-10ish mins.&lt;/p&gt;\n&lt;p&gt;Create a &lt;a href=&#34;https://core.telegram.org/bots/api&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;telegram&lt;/a&gt; node and select the appropriate Telegram account credential. Set the &lt;strong&gt;Trigger On&lt;/strong&gt; parameter to &lt;code&gt;Message&lt;/code&gt; so it triggers on new incoming messages of any kind (text, photo, sticker, etc.). Note that due to Telegram API limitations, you can only use one Telegram trigger per bot. Also ensure there is validation to only process messages from your known user ID, otherwise the bot will refuse to respond.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/telegram-trigger-receive.png&#34; alt=&#34;Telegram Trigger Settings&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Add an &lt;strong&gt;Analyze Text Message&lt;/strong&gt; node and connect it to your Google Gemini (or equivalent API) configuration. Set the Resource to &lt;code&gt;Text&lt;/code&gt; and Operation to &lt;code&gt;Message a Model&lt;/code&gt;, using the &lt;code&gt;models/gemini-2.5-flash&lt;/code&gt; model so its fast and reliable. models cheaper than 2.5-flash are not reliable based on testing. Within the node, establish a system prompt (Role: &lt;code&gt;Model&lt;/code&gt;) instructing the AI to process user input into structured nutritional data—identifying meal components, estimating portion weights, and warning it not to reveal its internal steps. Pass the user input as a prompt (Role: &lt;code&gt;User&lt;/code&gt;) using the expression &lt;code&gt;{{ $json.message }}&lt;/code&gt; along with today&amp;rsquo;s date context: &lt;code&gt;{{ $today.format(&#39;yyyy-MM-dd&#39;) }}&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/using-gemini-ai-via-api.png&#34; alt=&#34;AI Processing Configuration&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;To store the parsed responses, add a Google Sheets node configured to &amp;ldquo;Append&amp;rdquo; rows, and set it to &lt;strong&gt;Map Each Column Manually&lt;/strong&gt;. You can map fields originating from the trigger and model response:\n- &lt;strong&gt;User_ID&lt;/strong&gt;: &lt;code&gt;{{ $json.chat_id }}&lt;/code&gt;\n- &lt;strong&gt;Date&lt;/strong&gt;: Extract and format the date directly with &lt;code&gt;{{ DateTime.fromSeconds($json.date).format(&#39;yyyy-LL-d&#39;) }}&lt;/code&gt;.\n- Dietary metrics like &lt;strong&gt;Meal_description&lt;/strong&gt;, &lt;strong&gt;Calories&lt;/strong&gt;, &lt;strong&gt;Proteins&lt;/strong&gt;, &lt;strong&gt;Carbs&lt;/strong&gt;, and &lt;strong&gt;Fats&lt;/strong&gt; can be defined automatically by mapping them from structured outputs generated by the AI model.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/append-to-excel-sheet.png&#34; alt=&#34;Append to Google Sheets&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There is all sorts of existing nodes (in &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;, a &amp;ldquo;node&amp;rdquo; is basically a building block or a step in your automation workflow—it can be a trigger that starts the flow, an action that connects to a specific app&amp;rsquo;s API like sending a message, or a logic step that filters or processes data) that we can use. For this use case, reusing the &lt;a href=&#34;https://developers.google.com/sheets/api/guides/concepts&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google sheet&lt;/a&gt; node from official requires getting API keys, but that is well documented by &lt;a href=&#34;https://docs.n8n.io/integrations/builtin/credentials/google/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt; and &lt;a href=&#34;https://developers.google.com/workspace/guides/create-credentials&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;google&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;Finally, configure another Telegram node to send a reply back to the user. Set the Resource to &lt;code&gt;Message&lt;/code&gt; and Operation to &lt;code&gt;Send Message&lt;/code&gt;. For the &lt;strong&gt;Chat ID&lt;/strong&gt;, retrieve it from the initial trigger using &lt;code&gt;{{ $(&#39;Telegram Trigger&#39;).item.json.message.chat.id }}&lt;/code&gt;. Include the formatted Text from the AI calculation &lt;code&gt;{{ $json.message }}&lt;/code&gt;. Under Additional Fields, be sure to set &lt;strong&gt;Parse Mode&lt;/strong&gt; to &lt;code&gt;MarkdownV2&lt;/code&gt; so any formatting from the AI looks correct, and toggle off &amp;ldquo;Append n8n Attribution&amp;rdquo; for a cleaner message.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/send-telegram-msg-back.png&#34; alt=&#34;Send Telegram Summary Message&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For the initial setup, because it&amp;rsquo;s new, I was experimenting and took 4-5 hours, because it was interesting; but been using that for maintaining calories now; it&amp;rsquo;s great.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;AI is more prevalent these days, instead of building these automation no code solutions via these tools, users directly build with AI, there are pros and cons.. it&amp;rsquo;s already deployed on the server (the automation tool, one time setup), can deploy any number of times.. limit depends on the server limits, I&amp;rsquo;m okay.. but people need to know of how to debug in case of error, I mean there is AI for that too; if using &lt;a href=&#34;https://docs.n8n.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;n8n&lt;/a&gt;/automation.. everything is contained within this little confinement work area.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll be trying out other automation experiments, this is serving me well, recently I gained a lot of weight, though not overweight.. I still look fit from an outside view; but a little fat in stomach and back.. I started eating snacks every night and lots of oats (used to prepare the previous night with milk and sugar.. keep it in freezer).. the next morning eating it, I drove this diet for some time, it was good for my morning routine (not going to office on an empty stomach), but the problem was I gained like 5 kgs, I have never been this much weight in my entire life! but I&amp;rsquo;m 6ft.. so the weight is kinda explainable.&lt;/p&gt;\n&lt;p&gt;Me when 76kgs, ideal for my height, less fat content ratio in my body; this is before I got muscle buildup.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb-2026/me-when-76.jpg&#34; alt=&#34;image when I was 76kgs&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;but in terms of physique, , I got visibly more muscle development..after above image, idk.. started to go to gym again, let&amp;rsquo;s see, till now I&amp;rsquo;ve been continuing to do it every day except Sunday at home. But I&amp;rsquo;ll track the progress.&lt;/p&gt;\n&lt;p&gt;Updating this part as of March 2026, it&amp;rsquo;s happening again, I feel out of place, everyone around here is behind.. so behind .. than I expect them to be in, when I see, I see a fine line between things and it&amp;rsquo;s like I can slip in between them.. and go past beyond. I see it like this, a highway road that is busy with vehicles going left and right, but I see a path in slow motion that I can slip in between and move to the other side, I see what others are not seeing, IDK how long I will be blessed with this gift or curse that I have, can&amp;rsquo;t share this with anyone, I exposed this lil bit to sq#3, ~Future me is this happening again? Hope you landed on things, learned from past.&lt;/p&gt;</description></item><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item></channel></rss>"}, "8a6661632ddab34594cddf723aff07d27f107a16": {"t": 1785770747.5223231, "n": 170650, "h": "0af11c4e3e398f20", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Wed, 21 Jan 2026 17:35:16 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at most 3 apps..self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, buy a developer certificate.. the developer certificate is $100 per year or altstore, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it to buy a developer certificate.. otherwise it&amp;rsquo;s every year purchase.. you gotta do.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and use it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third-party App Stores in EU countries due to the &lt;strong&gt;Digital Markets Act (DMA)&lt;/strong&gt;. This officially went into effect on &lt;strong&gt;March 7, 2024&lt;/strong&gt;, though Apple announced the changes earlier in January 2024. But what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know it sounds sketchy, but it&amp;rsquo;s actually widely considered safe. AltStore is open-source, and your credentials are sent directly to Apple&amp;rsquo;s servers via an encrypted connection to handle the app signing process—they are not stored on any third-party servers. For peace of mind, many developers recommend using a secondary Apple ID specifically for sideloading, though using your main account with two-factor authentication (2FA) is also supported and secure. I checked the community (mostly reddit) and the project&amp;rsquo;s details, and it&amp;rsquo;s okeyish for my use.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; and their &lt;a href=&#34;https://faq.altstore.io/getting-started/how-to-install-altstore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Getting Started guide&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h3 id=&#34;key-features-of-altstore&#34;&gt;Key Features of AltStore:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Jailbreak Required:&lt;/strong&gt; Works on any iOS device running iOS 12.2 or later.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Wireless Refreshing:&lt;/strong&gt; Apps can be refreshed automatically over Wi-Fi when on the same network as AltServer.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Open Source:&lt;/strong&gt; The project is transparent and &lt;a href=&#34;https://github.com/altstoreio/AltStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;available on GitHub&lt;/a&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple.&lt;/p&gt;\n&lt;p&gt;There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the now-recommended &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection.&lt;/p&gt;\n&lt;h3 id=&#34;why-sidestore-is-better&#34;&gt;Why SideStore is better:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;No Computer Needed (mostly):&lt;/strong&gt; After the initial pairing file setup, you only need the phone and a Wi-Fi/Data connection.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Multiple Sources:&lt;/strong&gt; Supports adding custom community repositories (repos).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official Resources:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore Website&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/SideStore/SideStore&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SideStore GitHub&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.sidestore.io/docs/installation/install&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Setup Guide&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The pain is gone now.&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit). The way it works is by creating a virtual environment (like a container) for apps. You install all your IPA files inside LiveContainer, and it manages them without needing individual certificates for each.&lt;/p&gt;\n&lt;h3 id=&#34;features-of-livecontainer&#34;&gt;Features of LiveContainer:&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Bypass 3-App Limit:&lt;/strong&gt; Run as many apps as you want inside the container.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;JIT-less Mode:&lt;/strong&gt; Can run many apps without needing JIT enabled.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Isolated Data:&lt;/strong&gt; Each app gets its own separate data folder.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Official GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;khanhduytran0/LiveContainer&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;The caveat is that you can only run one application at a time with one LiveContainer instance, but since you can have multiple LiveContainer instances (if you really need to), the apps are not limited anymore.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essentials, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s an online golf game..that I used to play for a short period of time (&lt;strong&gt;Pangya&lt;/strong&gt;). Again randomly on one weekend, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat. The official company (&lt;strong&gt;Ntreev Soft&lt;/strong&gt;) has gone under/stopped services, so the official anti-cheat won&amp;rsquo;t work.&lt;/p&gt;\n&lt;p&gt;But the detail is I have a Mac but this needs Windows to run. It was built in the early 2000s era. For those interested, community servers like &lt;strong&gt;Pangya Reborn&lt;/strong&gt; or &lt;strong&gt;Pangya Debug&lt;/strong&gt; are still active.&lt;/p&gt;\n&lt;p&gt;Anyway, I was introduced to this game by my friend back in the old day. Now since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things. I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic &amp;amp; calming.&lt;/p&gt;\n&lt;p&gt;Spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run (reported by others), but I kept trying and trying and trying but it won&amp;rsquo;t work. Then finally I tried installing on my ARM Windows (Parallels) running on my Mac. For my surprise it runs great! I thought ARM machine won&amp;rsquo;t run this old x646 game, but it ran great. I was ecstatic, so I spent my time on that more than I thought I would be rediscovering the memories I had back when I was playing it, the items, the maps, the things.&lt;/p&gt;\n&lt;p&gt;But I didn&amp;rsquo;t want to go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/weeks are the best right, as on anything..you will have the honeymoon period, so I did put a quantitative chunk of that in the game.. like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;In this jounral, I started to do this new thing&amp;hellip; I started to do..write with my eyes closed and type, it&amp;rsquo;s much faster to write with the flow, I love it, any spelling / accuracy mistakes I correct it later, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item></channel></rss>"}, "0f122ca129d20b660689105961cd85b58f83deb5": {"t": 1785770749.3768167, "n": 167173, "h": "910a2ce9abde9cfd", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Wed, 21 Jan 2026 17:35:16 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Installing third party apps on IOS without jailbreaking</title><link>//bluepie.in/2026/01/jan-2026/</link><pubDate>Wed, 21 Jan 2026 17:35:16 +0000</pubDate><guid>//bluepie.in/2026/01/jan-2026/</guid><description>\n&lt;p&gt;Welcome to 2026, I once thought what it&amp;rsquo;s gonna look like when it&amp;rsquo;s 2024..2026.. when I was in 2017..2018. A lot has changed, I wouldn&amp;rsquo;t have even predicted the things that happened.&lt;/p&gt;\n&lt;h2 id=&#34;sideloading&#34;&gt;Sideloading&lt;/h2&gt;\n&lt;p&gt;Before I used to say you can&amp;rsquo;t sideload in iOS/Apple phones without jailbreaking. In a way that was dumb, because when you think about it.. iPhone apps have to be developed..obviously they can be tested on a physical device.. I didn&amp;rsquo;t think of that.. so.. I wasn&amp;rsquo;t thinking in this angle.&lt;/p&gt;\n&lt;p&gt;So when I started using iPhone, I got an iPhone mainly for app development, I soon realized you can use at least 3 apps self signed can be installed on the phone, but the catch is, you have to install it again and again after 7 days.. because it would expire, it&amp;rsquo;s a pain right.. I know, it is.&lt;/p&gt;\n&lt;p&gt;To solve this you have either two options, by a developer certificate.. the developer certificate is $100 per year, if you have an app published and used by lots of people and it gets you revenue.. it&amp;rsquo;s worth it.. otherwise it&amp;rsquo;s every year you gotta pay up.. which is horrible in my opinion. How it works is.. you get a developer license, so any apps you want to sideload.. you just sign without your certificate and reuse it as many times as you want.&lt;/p&gt;\n&lt;p&gt;Later Apple was forced to allow third party App Store in EU countries.. this happened in.. but what I&amp;rsquo;m saying is way before that&amp;hellip;&lt;/p&gt;\n&lt;h2 id=&#34;altstore&#34;&gt;Altstore&lt;/h2&gt;\n&lt;p&gt;I used to use AltStore to sideload apps on my iPad and iPhone, &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore&lt;/a&gt; comes in two parts, the server and the client, the server is an AltServer that runs on the Mac machine, at this point I had a Mac machine, but there is also a version for Windows and Linux I believe, you sign in to the AltServer, you have to give out your account email and password, .. yeah I know sketchy, but it works, I checked and it&amp;rsquo;s not harmless?? but I didn&amp;rsquo;t actually check the code, I believe the auth works in the client in the machine and then they capture the token for the account and then use it.&lt;/p&gt;\n&lt;p&gt;Once that is installed in the laptop or Mac machine. &lt;a href=&#34;https://altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Download AltServer here&lt;/a&gt;. It runs a small binary that runs the background that you seen the in the title bar, you click/ right click it and then install the AltStore, you can also download the open source version of apps you need like torrent, YouTube plus plus (a version of YouTube modified from original source code to add ad blocking and background play and additional features), this is really great, but the catch as I&amp;rsquo;ve said it can only have 3 apps, and for free accounts you have to refresh every 7 days. Otherwise you have to do the installing of the AltStore again, the beauty of it is, if you refresh the apps within 7 days, you have to be on the same Wi-Fi network for his to work but run the AltServer on the laptop and then click the refresh or you can also do it with cable, connecting the iPhone to the Mac machine, because the initial setup of the AltStore and the signing is what takes times, so if you can refresh every time before 7 days, you can just open the SideStore app on the phone and then refresh the apps, yea the app is used to check the logs and manage the apps in that are sideloaded.&lt;/p&gt;\n&lt;p&gt;For detailed instructions, checking out the &lt;a href=&#34;https://faq.altstore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AltStore FAQ&lt;/a&gt; is highly recommended.&lt;/p&gt;\n&lt;h2 id=&#34;evolution&#34;&gt;Evolution&lt;/h2&gt;\n&lt;p&gt;The evolution of this is eventually came by, I always knew other options are present like using leaked certificates of developers / companies and then use that essentially using it for a year. A lot of people would use it but I thought it was sketchy, there is also a version where you install the key (token;.. key pair) using the key install the app, but then they block the Apple server via DNS or VPN , I have my own DNS server so blocking only the Apple server for my device is easy, but as I said I thought it was sketchy.. I stuck to AltStore as its used by many / it widely accepted at some point since I daily drive the Android phone I started to watching YouTube videos and other things on my Android device so I started to use less and less the iPhone for entertainment or other things&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/sidestore.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://docs.sidestore.io/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official SideStore Documentation&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since the beginning I would miss some weeks were I forget to refresh then I had to reinstall the same steps over and over again, it was a pain but that is what I was doing until 2025, at one random night after midnight idk what came after me, I just started to look at subreddits of iOS just out of randomly, I don&amp;rsquo;t do that normally, then read about SideStore, so opened my laptop installed SideStore server, installed the SideStore app, they have this thing where if you install the SideStore app installed on your app then do some process to install something by connecting your device (generating a pairing file via AltServer or JitterbugPair and importing it into SideStore), you don&amp;rsquo;t have to connect your device to laptop again, you can just refresh the things on your phone it&amp;rsquo;s simple. There is an app on the App Store called &lt;strong&gt;WireGuard&lt;/strong&gt; (or the SideStore-specific &lt;strong&gt;LocalDevVPN&lt;/strong&gt;) that you can use to turn on the dev VPN. How it works is that SideStore uses a specially configured VPN profile (via WireGuard) to route traffic locally (localhost). This tricks iOS into thinking the device is connected to a computer running a signing server (like AltServer) via USB, allowing it to sign and refresh apps directly on the device without an actual computer connection. The pain is gone now,&lt;/p&gt;\n&lt;h2 id=&#34;about-the-3-apps-sideloading-problem&#34;&gt;About the 3 apps sideloading problem&lt;/h2&gt;\n&lt;p&gt;There is this awesome open source app called &lt;a href=&#34;https://github.com/khanhduytran0/LiveContainer&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LiveContainer&lt;/a&gt; (it allows you to run unlimited apps within a single app securely, bypassing the 3-app limit), the thing is it&amp;rsquo;s like a container of apps, that you can run, so you essentially install all the apps inside the LiveContainer and then when you want to any app, you just run them and the LiveContainer as its name states it runs that app when you open and close and minimize it all does it as the app, the LiveContainer is not LiveContainer..its your app, the caveat is that you can only run one application at a time with one LiveContainer, but yea, the apps are not limited anymore,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer.png&#34; alt=&#34;sidestore&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2026/livecontainer-settings.png&#34; alt=&#34;sidestore&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;automating&#34;&gt;Automating&lt;/h2&gt;\n&lt;p&gt;You still have to open the app (SideStore and then refresh the apps?) No that&amp;rsquo;s where the shortcuts come in . I&amp;rsquo;ve never mentioned these before there, but there are a great things that you can use to automate a lot of things, was initially impressed by them but failed to spend time on them to set it up, so I found this shortcut online Reddit of all place inspected them and then imported it, it basically runs ever based on configuration I set it up as 3 days every night, turns on Wi-Fi, connects to known Wi-Fi (phone does that automatically), connects to the dev VPN then then runs the short to refresh the app, the whole process is automated, you don&amp;rsquo;t even have to open the phone, if it&amp;rsquo;s being used it runs in the background once it&amp;rsquo;s done, it disconnects the VPN that&amp;rsquo;s all, if you want to run off VPN after it ran you just configure that too, for my iPad this is what I&amp;rsquo;ve done.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2026/Refresh-Sideloaded-Apps.shortcut&#34;&gt;Shortcuts File Download, you can review and modify them&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s been so long, I feel lost, mindless sometimes, I have so many things I want to do , but little time, I find it harder and farther to focus on the essential, I can&amp;rsquo;t believe after COVID, everything moved so fast, I don&amp;rsquo;t like it, but it is what it is, it&amp;rsquo;s already January, marriage topic comes up, still some issues on that side, then the AI topic comes up, I&amp;rsquo;m trying to pivot to it by learning from scratch (understand the core), studying in my free time, obviously I put a lot of time in new things by AI to build with it, automate it, use it to my benefit and learn new things every day or every other day, I put a lot of time in it but ultimately I don&amp;rsquo;t want to use it as a product, I want to learn from scratch understand to build ai from scratch. I have a new addiction playing an old game that I used to play in school? and maybe start of college, it&amp;rsquo;s a golf game again randomly in the evening or afternoon, searched for private server of the game, found it was running and there are some open source tools that make the game run without anti-cheat the official company that built that game has gone under, so the anti-cheat won&amp;rsquo;t work, .. but the detail is I have a Mac but this needs Windows to run, it was built in 2000&amp;rsquo;s eras before 2010? Can you believe lol.. yea, anywhere I was introduced to this game by my friend since I stopped playing Steam games, as it was taking much of my time, I thought I could play these games after I retire? so all the games I bought.. maybe I&amp;rsquo;ll play them after I retire? I do enjoy playing games, but I equally enjoy or enjoy more learning new things and building new things, I&amp;rsquo;ve noticed it&amp;rsquo;s been taking a lot of time recently, so I stopped that for almost 3 months? yeah it&amp;rsquo;s been three months so I thought this game could be therapeutic and started to dig in it, spent almost half a day on a weekend to set it up in on Mac with Crossover, it said game won&amp;rsquo;t run ,but I kept trying and trying and trying but it won&amp;rsquo;t work, then finally I just installed on my arm machine for my purpose it runs great I thought arm machine won&amp;rsquo;t turn the old x86 game, but it ran great, I was surprised, so spending my time on that more than I thought I would rediscovering the memories I had back when I playing it, the items, the maps. the things, didn&amp;rsquo;t know and go in the same road when I was in school.. meaning I don&amp;rsquo;t wanna put more time in it, but the first few days/ weeks are the work right, as you will have the honey moon period, so I did put a quantitative chunk of that in the game like small chunks here and there couple weeks, but yea. It was addicting for a short while, maybe I&amp;rsquo;ll post about it sometime in the next writing.&lt;/p&gt;\n&lt;p&gt;These new things I started to do, write with my eyes closed and type, it&amp;rsquo;s much more easy to write the flow, I love it, any spelling / accuracy mistakes I correct it later, I write so much like this, I have to look it up if other people have done the same before,.&lt;/p&gt;\n&lt;p&gt;Happy New Year, hope you are doing well.. (me reading from future, sq)&lt;/p&gt;</description></item><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item></channel></rss>"}, "fe242203f2a4c8da709a9c7f284000a7fdc171c4": {"t": 1785770751.07661, "n": 162746, "h": "b91cea4eca3124e7", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Thu, 25 Dec 2025 01:04:25 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Local Agent Workflows</title><link>//bluepie.in/2025/12/dec-2025/</link><pubDate>Thu, 25 Dec 2025 01:04:25 +0000</pubDate><guid>//bluepie.in/2025/12/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item></channel></rss>"}, "7a7e365f8f3d5a78f6eb458d7480c59a72955bbc": {"t": 1785770752.902909, "n": 162746, "h": "2093e68277267b14", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Mon, 12 Jan 2026 01:04:25 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Local Agent Workflows</title><link>//bluepie.in/2026/01/dec-2025/</link><pubDate>Mon, 12 Jan 2026 01:04:25 +0000</pubDate><guid>//bluepie.in/2026/01/dec-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been mentioning these things in my previous posts. I don&amp;rsquo;t use AI to write these, but I use them as a way to fix spelling and grammar mistakes. There is also another way that I use these tools for, and that is for local workflows. Local workflows help writing automation scripts in natural language; you don&amp;rsquo;t have to write code and handle edge cases to cover it. Give an example and it will take it over. Of course, I&amp;rsquo;m overseeing what code it&amp;rsquo;s actually running throughout the whole process.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/customisatons-menu.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/list-of-workflows.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-mean&#34;&gt;What I Mean&lt;/h2&gt;\n&lt;p&gt;Take Antigravity for example, but this works in VSCode Copilot, Cursor, etc. It&amp;rsquo;s like a shortcut key creation. Once you create it, you run it with &amp;ldquo;/&lt;name-you-gave-for-workflow&gt;&amp;ldquo;&lt;/p&gt;\n&lt;h2 id=&#34;creating-a-workflow&#34;&gt;Creating a workflow&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s as simple as instructing a person on a task; AIs have come so far in very little time (as of Dec, 2025).&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/automation.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Once the workflow is created, it&amp;rsquo;s simple to run it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/dec-2025/workflow-runs.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;So the office things came and went. December time usually there is a lot of free time, but this year, I have a deadline to work towards. I was assigned to a project and the deliverable is at Dec 29 for that microservice side. Though I have a ton of leaves, I can&amp;rsquo;t use them. The leaves will get wasted; I get 12 sick leaves, 12 casual leaves, then paid leaves. The sick and casual leaves don&amp;rsquo;t get transferred to the next year, so you have to use them within that year; they get credited at the start of every year.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been seeing a lot of bus accidents of late, mostly when I&amp;rsquo;m driving to and back from my hometown and where I work. I&amp;rsquo;ll attach a HandBrake-processed video here for future me for reference, so you can remember.&lt;/p&gt;\n&lt;p&gt;Happy New Year and stay safe; see you next year~&lt;/p&gt;</description></item><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item></channel></rss>"}, "0b79a5751157ead7e536386357cf89874e5f18f6": {"t": 1785770755.2056406, "n": 164685, "h": "8b0475d20671191f", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Tue, 18 Nov 2025 18:25:04 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(or)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;!-- ## Personal updates\nThe last month&#39;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&#39;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&#39;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &#34;Now you are the one to help us&#34;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.\nIt&#39;s amplified by this month&#39;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.\nThe ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&#39;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&#39;t expect these people to have the same level of dedication, integrity..etc --&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. &lt;!--- Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&#39;t I become my own boss? --&gt;&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;hld-i-m-planning&#34;&gt;HLD I&amp;rsquo;m planning&lt;/h2&gt;\n&lt;p&gt;Following is how I envision the request flows.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ratelimiting-hld.svg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item></channel></rss>"}, "87ea632dd16223e75aa5714ed5990d6af163e867": {"t": 1785770757.2774966, "n": 164532, "h": "ab1164d47f57adde", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Tue, 18 Nov 2025 18:25:04 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;The last month&amp;rsquo;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&amp;rsquo;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&amp;rsquo;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &amp;ldquo;Now you are the one to help us&amp;rdquo;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. After the vz announcement you will read below, for me atleast to see they are having the same kind of feeling they put the person within a short span of time was a suprise. I dont know/biast on whats the outcome will be, but yea was a suprise.&lt;/p&gt;\n&lt;p&gt;It&amp;rsquo;s amplified by this month&amp;rsquo;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, makeing progress every day, handling my releases well.&lt;/p&gt;\n&lt;p&gt;The ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&amp;rsquo;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&amp;rsquo;t expect these people to have the same level of dedication, integrity..etc&lt;/p&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&amp;rsquo;t I become my own boss?&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item></channel></rss>"}, "6ee0c679bf7fc04a70197c55ff03f4695abb2930": {"t": 1785770759.2817602, "n": 164398, "h": "2be0ec819634b48f", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Tue, 18 Nov 2025 18:25:04 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Custom CSS Utility - npm package - Open source</title><link>//bluepie.in/2025/11/nov-2025/</link><pubDate>Tue, 18 Nov 2025 18:25:04 +0000</pubDate><guid>//bluepie.in/2025/11/nov-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;For my projects, I use &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;reset.css&lt;/a&gt; and a set of utility classes without external libraries. (which I learnt from my prev company, I learnt a lot from there, when I joined a big company.. it was not the same, enterprise wise its different yea..sure but it has its pros and cons), I move them from project to project. This eliminates it and makes it easier to maintain and view the docs.&lt;/p&gt;\n&lt;p&gt;I wanted a minimal CSS utility that provides just basic features to get started quickly without the bloat of larger frameworks.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-did&#34;&gt;What I did&lt;/h2&gt;\n&lt;p&gt;Given the background and the context, I was in the process of changing the UI/Frontend of an existing project. I moved all the reset css to a file, then wrote all the utilities to a separate file.&lt;/p&gt;\n&lt;p&gt;Then came the idea of publishing the same, so I moved all the scss code from the main project, put it inside a new folder, initialised it with a package config and bundled it with &lt;a href=&#34;https://rollupjs.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;rollup&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;At this current state, I could not make it public because&amp;hellip; first I had to decouple the existing logic from the codebase to the project, then I looked at the docs and with help of ai, wrote helper functions and saw the standard and fixed my implementations and made it ready for publishing.&lt;/p&gt;\n&lt;h2 id=&#34;how-i-build-them&#34;&gt;How I build them&lt;/h2&gt;\n&lt;p&gt;It&amp;rsquo;s pretty straightforward, if you look at them.. gosh the tooling came a long way since the &lt;a href=&#34;https://gulpjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;gulp js&lt;/a&gt; tooling era.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;clean&amp;quot;: &amp;quot;rimraf dist docs/.vitepress/dist&amp;quot;,\n&amp;quot;build:theme&amp;quot;: &amp;quot;rollup -c&amp;quot;,\n&amp;quot;docs:dev&amp;quot;: &amp;quot;vitepress dev docs&amp;quot;,\n&amp;quot;docs:build&amp;quot;: &amp;quot;pnpm run build:theme &amp;amp;&amp;amp; vitepress build docs&amp;quot;,\n&amp;quot;docs:preview&amp;quot;: &amp;quot;vitepress preview docs&amp;quot;,\n&amp;quot;format&amp;quot;: &amp;quot;prettier --write .&amp;quot;,\n&amp;quot;build&amp;quot;: &amp;quot;pnpm run clean &amp;amp;&amp;amp; pnpm run build:theme &amp;amp;&amp;amp; pnpm run docs:build&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-to-install-use-it&#34;&gt;How to install &amp;amp; use it&lt;/h2&gt;\n&lt;p&gt;You can install with &lt;a href=&#34;https://www.npmjs.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npm&lt;/a&gt;; it&amp;rsquo;s here &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Use the following command with npm or other package managers, or you can even use a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/CDN&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;CDN&lt;/a&gt;,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link\nrel=&amp;quot;stylesheet&amp;quot;\nhref=&amp;quot;https://cdn.jsdelivr.net/npm/minimal-css-utility/dist/min.css&amp;quot;\n/&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;npm install minimal-css-utility\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then import this in your main css file,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;@import &#39;minimal-css-utility/dist/min.css&#39;;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;css-scoping&#34;&gt;CSS Scoping&lt;/h2&gt;\n&lt;p&gt;I added the support to have css scopes, what this means is I can have multiple css frameworks and work on the same codebase, my project won&amp;rsquo;t interfere with anything.&lt;/p&gt;\n&lt;p&gt;Read more on how to implement it here. I&amp;rsquo;ve added an example here for &lt;a href=&#34;https://react.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;react&lt;/a&gt;, but it should be the same for vanilla or other frameworks.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/getting-started/scoping.html&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-docs&#34;&gt;The docs&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://bluepie.in/minimal-css-utility/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Since I used &lt;a href=&#34;https://vitejs.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Vite&lt;/a&gt;, I saw the documentation system by them, it was quite nice to navigate through the docs and easy to setup, I find that sometimes I have to navigate to the codebase and look up a particular css class and probably change it, So I put the docs inside the same folder and wrote the generation scripts inside the &lt;a href=&#34;https://docs.npmjs.com/cli/v10/configuring-npm/package-json&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;package.json&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the docs was very fun, Include my css file and use it&amp;hellip; like how a user would. Because of this&amp;hellip; I was able to figure out\nmultiple wrong implementations, I was using them with wrong implementations all this time everywhere, I didnt realise it was wrong since I was just copying and moving these source files from one place to another.&lt;/p&gt;\n&lt;p&gt;check source examples here:\n- &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/blob/main/docs/utilities/sizing.md?plain=1&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;check some of the rendered examples there,\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/positioning.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/positioning.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/layout/grid.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/layout/grid.html&lt;/a&gt;\n- &lt;a href=&#34;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://bluepie.in/minimal-css-utility/utilities/flexbox.html&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Writing the above gave me the idea that this is how I would test this: visually identify the issues after taking build, I can just skim through the docs of the places where I made changes in source code of utility classes and check only those affected places in docs, since I used the build from the css utility class, these changes would come here.&lt;/p&gt;\n&lt;p&gt;I looked up online, major big css frameworks employ similar and better versions of what I just said and there are testing frameworks that just do that, for ex: &lt;a href=&#34;https://github.com/garris/BackstopJS&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BackstopJS&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-i-like-the-most-from-the-docs&#34;&gt;What I like the most from the docs&lt;/h2&gt;\n&lt;p&gt;I like the search functionality a lot, click search button or use the shortcut and starting to type, you see only the relevant information and takes me right to the spot, what and where I was looking.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov-2025/docs-ref.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-release-future-versions&#34;&gt;The release &amp;amp; Future versions&lt;/h2&gt;\n&lt;p&gt;Currently, it&amp;rsquo;s on npm and github, If I use additional things to my custom ui, I will add it here, make a new release, so it can be upgraded with version specific installs from npm. so it wont break anything.&lt;/p&gt;\n&lt;p&gt;npm: &lt;a href=&#34;https://www.npmjs.com/package/minimal-css-utility&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://www.npmjs.com/package/minimal-css-utility&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;github: &lt;a href=&#34;https://github.com/aghontpi/minimal-css-utility/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/minimal-css-utility/releases&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;The last month&amp;rsquo;s feeling of just an excel sheet because it happened to a person close to me. Their problem is still not resolved it&amp;rsquo;s the second half of Nov.. like heck?. the people who were in charge wrote a lame-ass email of the same thing after 1 month of delay, basically they came back after enjoying diwali and holidays, enjoying their leaves, personally I think I thought.. they just didn&amp;rsquo;t want to deal with all this. The managers who were idiotic saying to a person who was in that situation &amp;ldquo;Now you are the one to help us&amp;rdquo;, just after hearing the announcement, the numbness of that lady is outstanding, I hope she gets what she deserves, karma will come back for her. To see they are having the same kind of feeling they put the person within a short span of time was eye opening.&lt;/p&gt;\n&lt;p&gt;It&amp;rsquo;s amplified by this month&amp;rsquo;s announcement of VZ, search Vz, Nov, 2025 (me) you will remember and find it in news articles. Do you remember all the people packing? Im sure you do, For me, I was thinking I was doing my job, make progress every day, handling my releases well.&lt;/p&gt;\n&lt;p&gt;The ugly of corporate world, a lot of vendors make the company shit, the developers dont know what they are doing, they are just here for the paycheck with the minimal/ no amount of work, there is no engineering happening even in higher and lower levels, seeing all the frenzy when an announcement is made for budget cuts and etc, they started putting effort again.. I was expecting a level of standard and I can&amp;rsquo;t put a single word to explain it.. again going back.. I think its better for me to just be my own boss, I can&amp;rsquo;t expect these people to have the same level of dedication, integrity..etc&lt;/p&gt;</description></item><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&amp;rsquo;t I become my own boss?&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item></channel></rss>"}, "6b9139982d48a9ac57d865f2c46edc28c093aea7": {"t": 1785770761.1775918, "n": 166589, "h": "e33a1767948b43fb", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Tue, 14 Oct 2025 19:36:37 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>&#34;Slop Generators - Make no mistake, they cannot think&#34;, as of Nov 25th, 2025</title><link>//bluepie.in/2025/10/oct-2025/</link><pubDate>Tue, 14 Oct 2025 19:36:37 +0000</pubDate><guid>//bluepie.in/2025/10/oct-2025/</guid><description>\n&lt;h2 id=&#34;quote-i-liked-from-asahi-linux&#34;&gt;Quote I liked from Asahi Linux&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve read the quote, in an open source AI contribution page, exact word for word, &lt;a href=&#34;#Quote I liked from Asahi Linux&#34;&gt;quote&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;a href=&#34;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://asahilinux.org/docs/project/policies/slop/#its-just-matmul&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;It is very easy to get caught up in the hype that bad actors have built around Slop Generators. The anthropomorphic presentation of Slop Generators as &amp;ldquo;agents&amp;rdquo; or &amp;ldquo;assistants&amp;rdquo; is a very deliberate attempt to manufacture consent for their integration into workforces at the expense of human interaction. The implication of some higher degree intelligence or sentience is very much deliberate, and it is very much false.&lt;/p&gt;\n&lt;p&gt;Make no mistake, they cannot think. They cannot reason. They cannot take into account context. They don&amp;rsquo;t &amp;ldquo;know&amp;rdquo; things or have a sense of humour or any of the other human-centric qualities bad actors would have you believe of them. Slop Generators are a chain of matrices in a stochastic system. The output of a Slop Generator is nothing more than a statistical calculation, where the next word to be generated is decided by an opaque probabilistic function dependent on previously generated words. This is fundamentally the same mathematics that is used to predict the weather.&lt;/p&gt;\n&lt;p&gt;A Slop Generator cannot assess the veracity of its claims, nor can it ever tell you that it simply does not know something. Slop Generators are often confidently incorrect as a result, and require brow-beating to admit a mistake. They are therefore highly inappropriate tools in contexts where truth and correctness are of utmost importance, and when the user is not already highly knowledgeable and confident in the problem area. This presents a bit of an issue for Slop Generators; if the user is already highly knowledgeable and confident in the problem area, then why ask the Slop Generator in the first place?&amp;rdquo;&lt;/p&gt;\n&lt;h2 id=&#34;coming-to-reality&#34;&gt;Coming to reality&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been inspecting UI source code of the websites I visit, also the open source projects I see, I see the source code of them as well, it&amp;rsquo;s easy as of Oct 2025 to identify if it was generated by AI. Unless explicitly told not to generate things with Tailwind, it mostly will be Tailwind with its absurd twisted ways of using it. When you see it, you will know it&amp;rsquo;s not written by a human or even reviewed by a human.&lt;/p&gt;\n&lt;p&gt;We have to accept the fact, AI is everywhere, using it is productive, yes, but there has to be some oversight, otherwise.. it gets to the point of no return, when at one point in the future, everything is so convoluted by AI, humans can&amp;rsquo;t jump in between and fix it. I think of it like compilers, before people used to write machine code, then compilers came along and simplified the process of writing code by providing a sort of language that we can use to write the programs and code in. Now will the same happen with AI? Will it become some sort of a compiler for programs? At the current state.. I don&amp;rsquo;t see it happening, but when you describe it, the output it does produce, so it&amp;rsquo;s a compiler?? Weird to think about it.. isn&amp;rsquo;t it?&lt;/p&gt;\n&lt;p&gt;In news articles I see posts in between embedded in them, those are just random stories that are obviously AI generated.. just inserted so that people fall for it and click them. Next time I see it, I will take a screenshot and put it here.. heck sometimes even the news articles are entirely written by AI, I&amp;rsquo;m not even sure if they are getting reviewed by actual people.&lt;/p&gt;\n&lt;h2 id=&#34;will-i-use-ai-to-write-this-journal-for-me&#34;&gt;Will I use ai to write this journal for me?&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;m proud of is not using AI to write this blog/journal for me, I think there will be a time where written media, written by individuals will be sought after, because increasingly, the AI generated content is everywhere, eventually it might mimic human writing too. An update, I&amp;rsquo;ve found this after I wrote this: &lt;a href=&#34;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://youtu.be/vrTrOCQZoQE?si=ytVmNdmNRWPk6yBx&lt;/a&gt; (a video from Computerphile mentioning the same)&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking and experimenting with AI. In some posts, I&amp;rsquo;ve used AI to generate featured image for the post, the one at the top, but I stopped and went back to me making one, then used them to check spelling and grammar. I think on 1 or 2 posts I used to change some paragraphs or write a poem which conveys another meaning as well, but that is about it, I stopped writing after I did that, I literally stopped writing for several months after I did that, like I lost motivation.&lt;/p&gt;\n&lt;p&gt;I have a friend who uses AI to crank out articles in medium, like importnat things you should know in spring boot, using AI in spring boot, top &lt;sup&gt;10&lt;/sup&gt;&amp;frasl;&lt;sub&gt;20&lt;/sub&gt; most useful commands in git..etc and people read and like it too.. like how? U can ask ai the same thing.. but the thing is now AI can crawl things on the web, so its not like articles that people write are going to waste, its read by AI crawlers and given back to the users. Most people have transitionsed to using AI for getting their answer, they dont read blog/ go to forums anymore, unless its a specific niche or they have time to do the research, asking ai is faster and its giving an easy way out, and people&amp;rsquo;s brain will subconsioursly/ consiously choose the lazy route.&lt;/p&gt;\n&lt;h2 id=&#34;identifying-if-its-ai&#34;&gt;Identifying if its AI&lt;/h2&gt;\n&lt;p&gt;One thing I&amp;rsquo;ve found is that, if you inspect the source code of the applications, it can be easily identified that it&amp;rsquo;s AI, take Tailwind, at least till now, if you see some Cthulhu level sh** in your UI logic in Tailwind, it&amp;rsquo;s probably AI generated. I&amp;rsquo;m glad that I graduated just before the cusp of AI, enough to know what it&amp;rsquo;s like before AI and after AI, I have git commits and open source projects to prove!&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;You are just a row in an Excel sheet, I read this so long ago from Hacker News, since then I&amp;rsquo;ve read countless articles from Hacker News and elsewhere. Someone close to me, got the same treatment, they got the boot/ asked to resign because of some government rules that does not apply to them, the people who validated it are incompetent, If they had put the minimal amount of effort, checked the facts, they would have easily known its not true.. Welp.. I feel they and a lot of others are just leeches in the company. I see a lot of people like this who just coast or do nothing in the company and just get salary every month. Sometimes I feel like why am I even working here, what was my goal? why don&amp;rsquo;t I become my own boss?&lt;/p&gt;\n&lt;p&gt;That said, the only thing that matters is your integrity, what you feel about it and how you process it and how you justify it.&lt;/p&gt;\n&lt;p&gt;That being said, let&amp;rsquo;s end with a less serious note, I got a new monitor setup for my room, tried using 4k with my mac, it was god tier, I had so much space to work with, I got a 55inch mini led 144hz 4k display (monitor, but its a tv at that size), I sit reasonably far from the monitor, Everything is crystal clear, the responsiveness is awesome, you have to switch to pc mode (it has vrr). I loved working on that, when I came back to my chennai setup 27inch monitor, I couldn&amp;rsquo;t adapt to it for a week. I have so much productivity in that. Let me show you. Played some games on 4k, it was really good, if I was a small kid I would literally spend my whole day, but big buddy me, got responsibilities and grownup duties, I played ghost, its one of the best games imo, no comments on the 2nd ghost game though. I bought amazing spider man remake, played for a couple of hours, it was good, need to finish it after I retire? IDK maybe before that..&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/oct-2025/my-desk-setup-2025.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;It was day 1 after my new setup, my desk is still a mess&lt;/p&gt;</description></item><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item></channel></rss>"}, "616572dad2b21b28167284baffc6fe16a28a9633": {"t": 1785770763.139415, "n": 165403, "h": "256e27e1f66ae089", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sun, 28 Sep 2025 22:49:48 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Better Downloader Self Hosted</title><link>//bluepie.in/2025/09/september-2025/</link><pubDate>Sun, 28 Sep 2025 22:49:48 +0000</pubDate><guid>//bluepie.in/2025/09/september-2025/</guid><description>\n&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;\n&lt;p&gt;I Need a better downloader that is on a server somewhere, it should have segmented downloads, It can have mutliple connections to a single download file that can maximize download speeds, The second problem to solve is that the media server if something has to be downloaded and watched, it has to be a torrent file, if its a direct file, the server, currently I have to login to a server and download with wget or aria2 via terminal.&lt;/p&gt;\n&lt;p&gt;Finding the right solution to selfhost.&lt;/p&gt;\n&lt;p&gt;I know of &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aria2/aria2&lt;/a&gt; existence, I used to use this long ago when downloading things directly to server. it has a lot of features that I looked for and on top of that even more.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Command-line interface&lt;/li&gt;\n&lt;li&gt;Download files through HTTP(S)/FTP/SFTP/BitTorrent&lt;/li&gt;\n&lt;li&gt;Segmented downloading&lt;/li&gt;\n&lt;li&gt;and believe me there are lots more, check the official &lt;a href=&#34;https://github.com/aria2/aria2&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github repo link&lt;/a&gt;.. i&amp;rsquo;ve provided.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;the-frontend&#34;&gt;The frontend&lt;/h2&gt;\n&lt;p&gt;My thought process is this, this is a command line utility, how will I use it with gui..? there are already similar projects which are called frontends for an already existing terminal utilites. so a quick google search, lead me to this project. &lt;a href=&#34;https://github.com/mayswind/AriaNg?tab=readme-ov-file&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/mayswind/AriaNg?tab=readme-ov-file&lt;/a&gt; , I liked the ui and it proviced all the configs and how it can configured.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Iimage showing aria2 initial configuration&lt;/em&gt; notice the port of the service, its 443, since im running on https and behind reverse proxy. Notice the secret token, this is important, without this, the FE will fail to connect.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/initial-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;need-a-way-to-run-it&#34;&gt;Need a way to run it,&lt;/h2&gt;\n&lt;p&gt;I was ready to spend hours on this to configure, figure out how to integrate these too separate programs.. but then since it has a lot of popularity.. 10k+ starts on github, I searched for any existing solutions, that it might already have, which lead me to the finding of this project: &lt;a href=&#34;https://github.com/hurlenko/aria2-ariang-docker&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/hurlenko/aria2-ariang-docker&lt;/a&gt;, Now I have an existing project that I can self host. I just need to run it on my server with custom configurations and reverse proxy it on my existing server which has nginx running. at some point I have to check out and configure caddy.. but thats for another day.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing aria2 download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/download-link.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations&#34;&gt;Configurations&lt;/h2&gt;\n&lt;p&gt;I use podman to run all the services instead of docker, so based on the docker image, I came up with the following command to run it on my server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Docker/ Podman&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;podman run -d \\\n--name=aria2-ui-idm \\\n-e ARIA2RPCPORT=6800 \\\n-e RPC_SECRET=&amp;lt;REDACTED&amp;gt; \\\n-p 5050:8080 \\\n-p 6800:6800 \\\n-v /mnt/torrents:/aria2/data \\\n-v /var/oled/aria2/config:/aria2/conf \\\n--restart unless-stopped \\\ndocker.io/hurlenko/aria2-ariang:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next comes the nginx config, I use the following nginx configuration to reverse proxy the requests to the aria2 container. Port 5050 is the web ui port.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy requests to the Ariang Web UI\nlocation / {\nproxy_pass http://127.0.0.1:5050;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_read_timeout 999999999;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now the frontend has to connect to the aria2 rpc service, which is exposed on port 6800 on the container, so we are proxying that too.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; # Proxy WebSocket requests to the Aria2 RPC service\nlocation /jsonrpc {\nproxy_pass http://127.0.0.1:6800/jsonrpc;\nproxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection &amp;quot;upgrade&amp;quot;;\nproxy_set_header Host $host;\n# Set a long timeout for the RPC connection\nproxy_read_timeout 999999999;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I also configured ssl on the server, but ignoring it here on explanations.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How it works&lt;/h2&gt;\n&lt;p&gt;The FE layer connect with the terminal utility through post request or websocket, we have to configure the first time we land on the Frontend UI, these requests have to be authenticated and have a secret token, that must be configured on the server.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing download options&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;image showing segmented download&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/september-2025/downloading-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;\n&lt;p&gt;In combination with media server, torrent and downloader, Its easier for me to get anyting on the media server/ download anything that I need, I&amp;rsquo;ve also configured copy party on my server, so Its easy to manage, download/ uplaod files on my server now, I&amp;rsquo;ll write about it someday. A long waiting requirement that I&amp;rsquo;ve been putting off..is finally done.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;I thought of holding off marriage, due to recent events, I think its best that I didn&amp;rsquo;t rush through marriage and take decisions drastically (I&amp;rsquo;ve updated this section on oct 15&amp;rsquo;th 2025). As for my counter part, she agreed on the same, though I initially asked her to wait, she also thinks waiting for now is the best way forward.. for people who dont&amp;rsquo; follow or in some weird case.. if I dont follow.. read back the previous posts.&lt;/p&gt;\n&lt;p&gt;I want to look back to this one day in the future and add comments/ updates and see how I feel.&lt;/p&gt;</description></item><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-aug-2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/aug2025/gphotos-patched-aug-2025.apk.zip&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item></channel></rss>"}, "9795ea291a665e3d297ed8b20c18ec23d4981d67": {"t": 1785770765.6996663, "n": 167463, "h": "9fe0fd12e5544d9f", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Wed, 30 Jul 2025 23:53:43 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px; min-width:231px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture from a 2022 post, &lt;a href=&#34;https://aghontpi.github.io/2022/02/modding-a-macos-app/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;modding a macos app&lt;/a&gt;), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item></channel></rss>"}, "2b042727597f29d0b9682281317603d23d0cb38c": {"t": 1785770768.7485244, "n": 167209, "h": "68c24031715d1794", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Wed, 30 Jul 2025 23:53:43 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Using the best thinking models and not burn money (Jul 31st, 2025)</title><link>//bluepie.in/2025/07/july-2025/</link><pubDate>Wed, 30 Jul 2025 23:53:43 +0000</pubDate><guid>//bluepie.in/2025/07/july-2025/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been having a dilemma, I don&amp;rsquo;t want to pay open ai the monthly 20$ subscription, but I want to use the latest and greatest model on some of the problems I have to reduce time. How can I achieve this? I&amp;rsquo;ve already mentioned in a previous writing last year.. that I use open api keys. I started using mobile and desktop clients with just the api keys. Couple months ago OpenAI also reduced the pricing of o3 model by 80%, I&amp;rsquo;ve been using the ai very actively, trying to burn my credits as soon as possible.&lt;/p&gt;\n&lt;h2 id=&#34;deciding-on-a-budget&#34;&gt;Deciding on a budget.&lt;/h2&gt;\n&lt;p&gt;I decided to load my account with 6 dollars, on an estimate I thought I&amp;rsquo;d run out of credits within half a month. I can use o4-mini thinking for most tasks and o3 thinking model for only tough tasks questions. Nowadays I always default to thinking model o4-mini, it&amp;rsquo;s very cost effective. I told myself, I&amp;rsquo;m getting this alternate to one KFC ultimate savings bucket.&lt;/p&gt;\n&lt;h2 id=&#34;the-ui&#34;&gt;The UI&lt;/h2&gt;\n&lt;p&gt;The chatgpt UI is clean and gives more control for devs, I like this UI, unfamiliar people might need to get used to it. It even shows the thinking tokens, at the time of writing.. only the openai web portal and the official api client shows the thinking tokens, if you use third party clients (more about this later on this post) for chat api.. we will not get the thinking tokens, we will just see the output.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/OpenAI-api_view.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;using-the-right-models-for-the-right-usage&#34;&gt;Using the right models for the right usage,&lt;/h2&gt;\n&lt;p&gt;I exclusively used openAI models alone, I stopped using google AI Studio and claude&amp;hellip; more context. I used google AI studio and claude occasionally to validate my solutions.. like my writing from last month about Ratelimiting with firestore.&lt;/p&gt;\n&lt;p&gt;In chatgpt we have different models, For most of my use cases I use o4-mini, at the time of writing (31st july, 2025).. the latest thinking models from Open AI are O3 and o4 mini-high, I&amp;rsquo;ve created a table to compare the pricing from openAI portal &lt;a href=&#34;https://openai.com/api/pricing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;OpenAI API Pricing&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;(maybe below pricing details act like a snapshot in future&amp;ndash;for me or anyone&amp;ndash;looking back 10/20/..etc years from future)&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o3&lt;/td&gt;\n&lt;td&gt;Most powerful reasoning model with leading performance on coding, math, science, and vision&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;OpenAI o4-mini&lt;/td&gt;\n&lt;td&gt;Faster, cost-efficient reasoning model delivering strong performance on math, coding and vision&lt;/td&gt;\n&lt;td&gt;$1.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.275 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$4.40 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;&lt;strong&gt;Non thinking models&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Description&lt;/th&gt;\n&lt;th&gt;Input Price&lt;/th&gt;\n&lt;th&gt;Cached Input Price&lt;/th&gt;\n&lt;th&gt;Output Price&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1&lt;/td&gt;\n&lt;td&gt;Smartest model for complex tasks&lt;/td&gt;\n&lt;td&gt;$2.00 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.50 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$8.00 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 mini&lt;/td&gt;\n&lt;td&gt;Affordable model balancing speed and intelligence&lt;/td&gt;\n&lt;td&gt;$0.40 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.10 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$1.60 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;GPT-4.1 nano&lt;/td&gt;\n&lt;td&gt;Fastest, most cost-effective model for low-latency tasks&lt;/td&gt;\n&lt;td&gt;$0.100 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.025 / 1M tokens&lt;/td&gt;\n&lt;td&gt;$0.400 / 1M tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;I always use thinking model.. cuz I think I am biased towards that, I need to change my view on this lol, my thinking is if reasoning model should be always better.. but I have seen videos from bytecloud (youtube) and reddit, that sometimes..it&amp;rsquo;s not always the case.&lt;/p&gt;\n&lt;h2 id=&#34;reason-i-don-t-use-openrouter&#34;&gt;Reason I don&amp;rsquo;t use OpenRouter&lt;/h2&gt;\n&lt;p&gt;The reason I don&amp;rsquo;t use OpenRouter is because of the money topup with tax, let me explain. When I load money in OpenAI api, I have a trick and can load all the money without tax, but when I try the same thing with OpenRouter.. it doesn&amp;rsquo;t work. so that&amp;rsquo;s the only reason I&amp;rsquo;m not using OpenRouter. One thing to point out is I do use OpenRouter but I&amp;rsquo;m not loading money into it.. There are free reasoning models on OpenRouter, I&amp;rsquo;ve tried them like &amp;ldquo;DeepSeek: DeepSeek V3 0324 (free)&amp;rdquo; which is completely free and works great, but it&amp;rsquo;s just for testing.&lt;/p&gt;\n&lt;h2 id=&#34;about-perplexity&#34;&gt;About perplexity,&lt;/h2&gt;\n&lt;p&gt;Perplexity made a stunt in India as far as I know.. I don&amp;rsquo;t know about other countries, they partnered with Airtel (a telecommunications company), they made a offer for all existing airtel users of all plans irrespective of prepaid/ postpaid/ broadband/ DTH&amp;hellip;etc and no restrictions on plans, that the users can claim perplexity Pro for 1 year at no charge. I always thought OpenAI, Anthropic (claude) &amp;amp; Google all caught up to Perplexity&amp;rsquo;s features.. and thought its userbase is declining, cuz the main feature of perplexity is the search integration with AI, all others major companies now have that option, I personally think this is a desperate move with fleeting userbase.. To harvest more data and use it? or show userbase is high to investors, though they said they had future funds for many more years.. idk im looking too deep into this. I tried to use it for some applications, it was okayish but not useful all the time. Recently you would have known I switched all my mobile numbers and broadband to jio.. not all, I still had one private number and my mom&amp;rsquo;s number didn&amp;rsquo;t get port over cuz of some complication due to I was not there while doing the porting (now she is on a year plan in airtel, still 10 months left I think)..so I used my mom&amp;rsquo;s number to claim the reward.&lt;/p&gt;\n&lt;p&gt;But I don&amp;rsquo;t think they are using the models that is selected in pro mode under the hood.. at least not all the time, because the results are bad, when compared with the result of Open AI o3 (at the time of writing from before), the results form Open AI are better, You have to trust my word on this one, I didn&amp;rsquo;t save my responses, but maybe I can do that as part of another writing, or maybe you can have a personal test as well.&lt;/p&gt;\n&lt;h2 id=&#34;ai-in-office&#34;&gt;AI in Office&lt;/h2&gt;\n&lt;p&gt;In office at the time of writing this, I/ you work at VZ, we have gemini pro included (2.5 pro is the one that latest from google gemini) and github copilot. You can use it for any amount without limits. In github copilot there are lots of limitations inside the enterprise.. many public features are not even available inside, like mcp servers and custom chat modes, all the publicly available models, and even sometimes when the output of the AI is being generated.. the message from model gets flagged and the output is removed from the chat.. saying it contains public code and asking us to retry with different prompt. The company (leaders/ managers) is also hardly pushing for using AI for everything.. Even before they are saying.. I&amp;rsquo;m already on the train, I was on the first members to get access to github copilot and was using ever since. I was one of the beta testers for copilot inside vz as well, I still have that public github public account that we were testing it with. I provided many feedbacks during that time.. don&amp;rsquo;t know if its valuable for them or not.. during this period I changed entire teams/ organisations and went on promotion etc.&lt;/p&gt;\n&lt;h2 id=&#34;using-openai-with-third-party-clients&#34;&gt;Using OpenAI with third party clients,&lt;/h2&gt;\n&lt;p&gt;I tried several self hosted opensource AI solutions so I can host on my server and then I can configure different API keys, be that may OpenAI, google or Anthropic. I tried running them in local but none of them satisfied my requirements, at the very end I choose to look for clients, apps on mac and iphone and found opencat &lt;a href=&#34;https://opencat.app/en/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;open cat&lt;/a&gt;, its lightweight and really fast and met all my requirements I use the free tier of the app, I can always mod the application as well if I want.. instead of paying monthly/ one time subscriptions.&lt;/p&gt;\n&lt;p&gt;You can configure different default models, currently I have o4 mini as the default model.&lt;/p&gt;\n&lt;div style=&#34;display:flex; gap:10px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-1.PNG&#34; style=&#34;height:500px;&#34; alt=&#34;OpenCat screenshot 1&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-2.PNG&#34; style=&#34;height:500px;&#34; alt=&#34;OpenCat screenshot 2&#34; /&gt;\n&lt;/div&gt;\n&lt;div style=&#34;display:flex; gap:10px; margin-top:60px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-3.PNG&#34; style=&#34;height:500px;&#34; alt=&#34;OpenCat screenshot 3&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/july-2025/open-cat-4.PNG&#34; style=&#34;height:500px;&#34; alt=&#34;OpenCat screenshot 4&#34; /&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;So about the the 6$ I loaded up for AI usage, I got two months of usage out of it.. which is a lot compared to the 20$ monthly price tag by Open AI for chatgpt plus or more costlier tiers. The key is to be smart for on which model to use and how to consume them.. even if over reliant on ai models on certain tasks, use them smart by not exceeding the budget. During these two months I&amp;rsquo;ve also used it to create images and experimented with audio as well.&lt;/p&gt;\n&lt;p&gt;I used around 300 requests, that accounts for about 2 million input tokens, again these are a mixture of all models.. like I said I only preferred to use reasoning models.. but a combination of different reasoning models.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/openai-ai-input-token-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Usage based on month-wise, openai&amp;rsquo;s month-wise usage report is weird, you can&amp;rsquo;t see the entire graph, you are literally given a sliding window of only one month..where you can see the requests.&lt;/p&gt;\n&lt;p&gt;My 6$ I was able to use it for 2 months and more.. I only counted till 5$, If I didn&amp;rsquo;t use it exclusively and the experiment it would have lasted even more.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/july-2025/month-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Continuing to not use the time tracker from last month&amp;rsquo;s suggestion, its bit of a hassle, in between I tried to write everything physical (pen and paper), because I was forgetting the tasks too focused on one thing and forgetting, As an adult I have a lot of things to do all the time. Gotta reason with my decisions and spend time on things appropriately.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been getting into reverse engineering in the last 30 days, it started off because I wanted to place designs on &lt;a href=&#34;https://www.autodesk.com/products/autocad/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AutoCAD&lt;/a&gt;, My usage is only minimal, so I don&amp;rsquo;t want to pay for it (even the civil engineers that live here don&amp;rsquo;t pay for autocad), but I also don&amp;rsquo;t want to use cracks from the internet for the latest versions, I can crack it myself with enough effort and use it, the one thing that is different is.. I have to mod the apps on mac (previously I wrote on how to mod apps for macOS, but that was done on intel or x86 architecture), that too on arm/ apple silicon. That was a learning curve for me, I also have to bypass the &lt;a href=&#34;https://support.apple.com/en-us/HT204899&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;SIP&lt;/a&gt; on macOS, disable it by booting into recovery mode/ keeping it active and patching it, debugging applications and cracking is only possible with disabling SIP because macOS won&amp;rsquo;t allow to debug application if that is turned on. I can do static analysis patch it that way, sign it with my custom certificate and run it..etc, I want to write about it separately.. Long story short, I made a patch that after checking if trial expired, instead of quitting, I made it so..its doesn&amp;rsquo;t quit. I also blocked internet for the cad software and patched the function that checks if trial license to false. This made the application work and I can use it without internet and all features work, the application does not quit, not does it complain about not having a license &amp;ndash; So going back to reverse engineering was lot of fun and I still want to do it..everyday, I&amp;rsquo;m trying to find time everyday to do more things. One thing I found out was I need cad architecture, its only available on windows, my civil friend told me to use that software to design. I daily drive a apple silicon mac nowadays, so I had to run windows on it, surprisingly there is &lt;a href=&#34;https://www.parallels.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Parallels&lt;/a&gt;, I tried the trial version and I tried windows 11 arm version on it and installed architecture trial (x86 version of this software) inside it and it worked, got no errors. Since I&amp;rsquo;m running that inside a VM I don&amp;rsquo;t have to patch it myself and spend another whole lot of time, I can use patches from the internet.. but the one thing that I have to patch is Parallels. currently its on trial period and works well.&lt;/p&gt;\n&lt;p&gt;Writing helps alleviate a lot of things, future me reading. hope you got through this situation, My current plan is to write more. Also writing without ai support make me write more things. The things I&amp;rsquo;m writing are more of a personal journal so I don&amp;rsquo;t care about the quality.&lt;/p&gt;</description></item><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item></channel></rss>"}, "ce2ffd56c513be8fd7321ff7a5a5f655bb723594": {"t": 1785770770.5999894, "n": 158640, "h": "3cac1df6be95977d", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Mon, 30 Jun 2025 11:33:00 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Building an IP Based Firebase Ratelimiter</title><link>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</link><pubDate>Mon, 30 Jun 2025 11:33:00 +0000</pubDate><guid>//bluepie.in/2025/06/building-an-ip-based-firebase-ratelimiter/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I have a project that I wrote 7 years ago, its still running on firebase.. no hickups (I had to occusionally update to a new node lts environment).. but it still worsks, at the time of writing/ implementing, ratelimiter was not implemented, So everyonce in a while I see the request logs and I see some IPs hitting the server with 1000s of requests, judging from the rquest.. its easy to distinguish them as bots and scanners. I have to weed them out, but I have to write it in a way that will not use unnecessary firestore reads and writes. (yea I also used firestore for this probject)&lt;/p&gt;\n&lt;h2 id=&#34;constraints-before-going-in&#34;&gt;Constraints before going in&lt;/h2&gt;\n&lt;p&gt;I have to make sure the firestore reads and writes are minimal, one read + one write per request, make sure automatic TTL purging keeps the firestore collection small.&lt;/p&gt;\n&lt;p&gt;I have no Redis / Memcached layer here, so I can&amp;rsquo;t use them..(using them would be the best solution)&lt;/p&gt;\n&lt;h2 id=&#34;schema-thats-need-to-be-changed-in-firestore&#34;&gt;Schema thats need to be changed in firestore&lt;/h2&gt;\n&lt;p&gt;A new collection called &lt;code&gt;ip_throttle&lt;/code&gt;, we will use the document ID as the client IP address.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;ip_throttle/\n└─ {ipAddress} # document ID == client IP\n• count number # requests in current window\n• windowStart timestamp # start of the rolling window\n• expiresAt timestamp # TTL field (auto‑deleted)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We can leverage the &lt;a href=&#34;https://cloud.google.com/firestore/native/docs/ttl&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Firestore TTL&lt;/a&gt; to automatically delete documents after the marked time, I have to mark my field &lt;code&gt;expiresAt&lt;/code&gt; in the TTL policy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ttl_policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;configurations-needed&#34;&gt;Configurations needed&lt;/h2&gt;\n&lt;p&gt;Follwing new configurations are needed, these doen&amp;rsquo;t involve code changes, In 5 minutes, we have 100 requests per IP.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:set \\\nratelimit.window_seconds=3600 \\\nratelimit.max_hits=100\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;For local emulation we can utilise&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firebase functions:config:get &amp;gt; .runtimeconfig.json\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;implementing&#34;&gt;Implementing.&lt;/h2&gt;\n&lt;p&gt;Nowadsys I dont care if the project was in TS or not, I just choose js and go with it, back then I was still using TS, so I have to write the code in TS.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;\n// abstraction to Firestore\nconst db = getConnection();\nexport async function rateLimiter(\nip: string,\nwindowSeconds = 3600, // just incase config does not exist\nmaxHits = 100 // just incase config does not exist\n): Promise&amp;lt;boolean&amp;gt; {\nconst now = Timestamp.now();\nconst ref = db.collection(&amp;quot;ip_throttle&amp;quot;).doc(ip);\nreturn db.runTransaction(async (tx: Transaction) =&amp;gt; {\nconst snap = await tx.get(ref);\n// this is the first request we get from this IP\nif (!snap.exists) {\ntx.set(ref, {\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n});\nlogger.info(`IP ${ip}: first hit, record created.`);\nreturn true;\n}\nconst data = snap.data()!;\nconst ageMs = now.toMillis() - data.windowStart.toMillis();\n// if the window has expired, reset it\nif (ageMs &amp;gt; windowSeconds * 1000) {\ntx.set(\nref,\n{\ncount: 1,\nwindowStart: now,\nexpiresAt: Timestamp.fromMillis(\nnow.toMillis() + windowSeconds * 1000\n),\n},\n{ merge: true }\n);\nlogger.info(`IP ${ip}: window reset.`);\nreturn true;\n}\n// if the request is within the window and within limit.\nif (data.count &amp;lt; maxHits) {\ntx.update(ref, { count: FieldValue.increment(1) });\nlogger.debug(`IP ${ip}: allowed (${data.count + 1}/${maxHits}).`);\nreturn true;\n}\n// if the request is over the limit\nlogger.warn(\n`IP ${ip}: throttled (${data.count}/${maxHits} in ${windowSeconds}s).`\n);\nreturn false;\n});\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;em&gt;Now to configure in router and sending out the response&lt;/em&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt; // existing code\nconst cfg = process.env;\nconst windowSeconds =\nNumber(cfg.RL_WINDOW_SECONDS) ||\nNumber(cfg.RATELIMIT_WINDOW_SECONDS) ||\n3600;\nconst maxHits =\nNumber(cfg.RL_MAX_HITS) ||\nNumber(cfg.RATELIMIT_MAX_HITS) ||\n5;\n// check for ratelimit\nif (!(await rateLimiter(ip, windowSeconds, maxHits))) {\nres.status(429).json({ error: &amp;quot;Too many requests. Try again later.&amp;quot; });\nreturn;\n}\n// original code\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;in-action-in-production&#34;&gt;In action in production&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/june-2025/ip_throtle_in_action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;The solution I&amp;rsquo;ve implemented is simple, effective and horizonally scalable all within firebase. No extra services, minimal reads and writes. I was able to implement and deploy to production in one single sitting. I had validated this with ai thinking models to find downsides, so it gave me a motivation to complete it quickly.&lt;/p&gt;\n&lt;h2 id=&#34;personal-updates&#34;&gt;Personal updates&lt;/h2&gt;\n&lt;p&gt;Like with tradition, I have the personal updates here, I moved the personal section from top to the end of the post over the years. I wana write/ share soo much more.. remind myself more. Been travelling back and forth from work location &amp;amp; home, Been busy with work a lot, though I shouldn&amp;rsquo;t take it too seriously, but when its a new skill, im all up for it, even if its people or project management, the one thing that pains is that there is a onshore team and sync with them is a pain (currently at the time of writing this you work in VZ), evey week, every months feels like an instant, its monday.. then its already friday, then its friday night start.. then its sunday night again with an instant. Feels like a cycle, The older I get, time does feel to go a lot faster. I even stopped playing the occasional gaming on the side.&lt;/p&gt;\n&lt;p&gt;My timer tracking project (didnt make it open source yet) was a lot helpful over the months, but I think im lossing a grip on it, its too micro managementy, everytime I do something, I have to open and start and end it, feels like..its taking lot of time just to do that between each teask. So what I&amp;rsquo;ll do is stop using the time tracking app that I built for 1 or 2 months and see how I fare, If I can still be productive without it and can remeber and complete tasks, then its great..future me how is it going?&lt;/p&gt;\n&lt;p&gt;Personally I have commitments now and that too is a time sink, Im not keeping tabs on how much time Im spending, I think to myself on heindsight at the end of the week or during self refelction, small small actions, small pieces of time each day over the week.. adding up.. is a lot of time. but I can&amp;rsquo;t ignore it can I, now I know why my friends&amp;rsquo; changed behaviours is attributed to. Its nobody&amp;rsquo;s fault, thats just life.&lt;/p&gt;\n&lt;p&gt;The AI slop is all over the place with the internet, its the worst. Manytimes I wonder.. I think for some/ many tasks, If people didn&amp;rsquo;t just overrely on AI, they can complete the job/ task way faster. Again on some tasks, its way easier with AI and reduces lot of time.&lt;/p&gt;\n&lt;p&gt;Another trend Im noticing is, people are too much relient on AI to write a feature in a product or entire codebase/product. When something comes up later on the project, they don&amp;rsquo;t know howt to fix it, I&amp;rsquo;ve seen it inside office projects (migration of entire codebase with AI and the people sturggle later), on the internet and word of mouth. Future me.. how its it going?&lt;/p&gt;\n&lt;p&gt;About the thing on may.. I&amp;rsquo;d be lying If I said its good/ going great. It is what it is, Trust on the process, I have to keep doing what I do, keep going, keep moving forward, things will fall in place, For now.. For this moment, I know, I have to make the right decision (Future me remember?) and the right decision I know is to stay without making any rash decisions..me reading..what happpended?..hope I/we are in a better place then..when are you reading this? 20&amp;rsquo;s, 30&amp;rsquo;s, 40&amp;rsquo;s?.&lt;/p&gt;</description></item><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;source-code-and-chrome-web-store&#34;&gt;Source code and Chrome Web Store&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Firefox Add-on:\n&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/tab-options-open-source/&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item></channel></rss>"}, "03741369c0b090a8c6f5e2871dc4dbf1f96c525d": {"t": 1785770772.5027854, "n": 160524, "h": "09669086b79b4882", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sun, 25 May 2025 21:22:39 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>MVP vs SLC - Fixing Duplicate Tabs</title><link>//bluepie.in/2025/05/fixing-duplicate-tabs/</link><pubDate>Sun, 25 May 2025 21:22:39 +0000</pubDate><guid>//bluepie.in/2025/05/fixing-duplicate-tabs/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I’ve been running into duplicate-tab problems across both my personal and office browsers. I needed a simple application that can detect duplicates and show a minimal UI to merge, close, or keep them.&lt;/p&gt;\n&lt;h2 id=&#34;mvp-vs-slc&#34;&gt;MVP vs SLC&lt;/h2&gt;\n&lt;p&gt;I’ve followed the MVP approach for years, but this time I went with SLC: &lt;strong&gt;Simple, Lovable, Complete&lt;/strong&gt;. Rather than shipping a bare-bones, bug-ridden MVP, I wanted something small and meaningful that people genuinely love—and that feels complete out of the box.&lt;/p&gt;\n&lt;p&gt;As the main user of this tool, my core requirement was clear&amp;hellip; whenever I open a tab that already exists—no matter which window(same profile, different profile&amp;hellip;etc) it’s in—I want to be notified immediately, so I can either jump to the original or keep both if I really need two copies.&lt;/p&gt;\n&lt;h2 id=&#34;designing-the-initial-version&#34;&gt;Designing the initial version&lt;/h2&gt;\n&lt;p&gt;For the first release, I kept everything ultra-lightweight (under 30 KB) and focused on a few key flows:&lt;/p&gt;\n&lt;p&gt;• Duplicate-Tab Detection&lt;br /&gt;\n– I scan all open tabs using the Chrome Tabs API.&lt;br /&gt;\n– If the current tab matches an existing URL, I show a toast that lets you &lt;strong&gt;Merge (Switch &amp;amp; Close)&lt;/strong&gt; or &lt;strong&gt;Keep &amp;amp; Go Back&lt;/strong&gt;.&lt;br /&gt;\n– Dismissing the toast with the X still updates a badge counter showing your total duplicates.&lt;/p&gt;\n&lt;p&gt;• Badge &amp;amp; Popup Panel&lt;br /&gt;\n– The badge counter turns red when duplicates exist.&lt;br /&gt;\n– Clicking the extension icon opens a popup listing every open tab.&lt;br /&gt;\n– From that panel you can &lt;strong&gt;Close&lt;/strong&gt;, &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;, or &lt;strong&gt;Save all &amp;amp; Close&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;• Session Save / Restore&lt;br /&gt;\n– One-click &lt;strong&gt;Save all tabs &amp;amp; Close&lt;/strong&gt; to snapshot your session.&lt;br /&gt;\n– Later, press &lt;strong&gt;Reopen all tabs&lt;/strong&gt; to bring everything back.&lt;/p&gt;\n&lt;p&gt;• Import / Export / Share&lt;br /&gt;\n– Export saved sessions to an HTML file.&lt;br /&gt;\n– Import them back on any machine and merge with your current tabs.&lt;/p&gt;\n&lt;p&gt;• Duplicate Tabs Summary&lt;br /&gt;\n– A separate block at the top of the popup shows all duplicate groups so you can close them in bulk.&lt;/p&gt;\n&lt;h2 id=&#34;next-version-v1-3-0-and-beyond&#34;&gt;Next version: v1.3.0 and beyond&lt;/h2&gt;\n&lt;h3 id=&#34;v1-3-0-highlights&#34;&gt;v1.3.0 Highlights&lt;/h3&gt;\n&lt;p&gt;• Clickable Tabs&lt;br /&gt;\n– You can now click any tab in the extension UI to navigate directly.&lt;br /&gt;\n• URL Handling&lt;br /&gt;\n– Fixed duplicate detection for URLs lacking &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;br /&gt;\n• Group Counts&lt;br /&gt;\n– Every tab group shows a count badge.&lt;br /&gt;\n• Fullscreen Dashboard Link&lt;br /&gt;\n– “Open in fullscreen-mode” in the popup reuses an existing fullscreen window.&lt;br /&gt;\n• Save All &amp;amp; Close / Restore All&lt;br /&gt;\n– Quickly snapshot or restore entire sessions.&lt;br /&gt;\n• HTML Import / Export&lt;br /&gt;\n– Share sessions as HTML files.&lt;/p&gt;\n&lt;h2 id=&#34;relevant-links&#34;&gt;Relevant links&lt;/h2&gt;\n&lt;p&gt;Source code:&lt;br /&gt;\n&lt;a href=&#34;https://github.com/aghontpi/Tab-Options&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://github.com/aghontpi/Tab-Options&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Chrome Web Store:&lt;br /&gt;\n&lt;a href=&#34;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/tab-options/kafdoidjnnbjciplpkhhfjoefkpfbplj&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Sometimes small things you do, doesn&amp;rsquo;t feel like a big thing.. but it does have a significant impact on our lives. The feeling of why am I even doing this..like your brain is somehwere else but still gotta push through everyday..if its study/ work..etc&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;To a lost soul&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When its hard, try foucs on things and writing. Read the book I got, thats all I got now, as im writing now at 2:01am. Its tough, My left eye twitching, everytime during the day when my eye twitch (which ______ said might be because of stressed about thinking about something, but I think it might be because you are going through something), I think you are going through the same stress &amp;amp; punishment but about 1000x more.&lt;/p&gt;</description></item><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item></channel></rss>"}, "c1efc716546f00d1e8eae17be11e1216ddb878ef": {"t": 1785770774.6869326, "n": 169234, "h": "1db3daaa256a9768", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sun, 27 Apr 2025 10:18:56 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. &lt;!---I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&#39;rd Frost.)---&gt;I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. &lt;!---Since I&#39;m planning to vacate Chennai in March, after February (Frost&#39;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.---&gt;&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can automate things, I never would have thought DNS provider would have full api access and are allowing acess to anyone who are buying domains.&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item><item><title>Updating Dns Servers for Ads and Tracking Protection in 2023</title><link>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</link><pubDate>Sat, 28 Jan 2023 06:02:22 +0000</pubDate><guid>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</guid><description>\n&lt;h2 id=&#34;upgrading-pi-hole&#34;&gt;Upgrading PI hole&lt;/h2&gt;\n&lt;p&gt;Once every year, I do server maintainence (during Dec-Jan), this year I updated the our dns server from &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;pi-hole&lt;/a&gt; to &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AGH&lt;/a&gt; (both are open source projects..) Just wanted to try an alternative, after using it for a while, the UI &amp;amp; Features on AGH is much better than pi-hole.&lt;/p&gt;\n&lt;h2 id=&#34;why-a-custom-dns-server&#34;&gt;Why a custom dns server?&lt;/h2&gt;\n&lt;p&gt;All websites/apis are just ip addresses (just numbers). Nameservers maintains a mapping of the domain names to the ip addresses, so when we make a request, we get the appropriate ip address.&lt;/p&gt;\n&lt;p&gt;The idea is simple, by running our own dns server, we can prevent our network providers spying on us, on top of that we can intercept &amp;amp; block all ads &amp;amp; trackers.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;I get ads, but what are Trackers?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Similar to ads, ever been in a situation where you searched for something in flipkart/amazon.. then later you find the ad to buy the product elsewhere, this is because of trackers.. trackers are worse because they collect your data, the same way we block ads, trackers should be blocked.&lt;/p&gt;\n&lt;h2 id=&#34;how-this-can-help-us-with-ad-trackers-blocking&#34;&gt;How this can help us with ad &amp;amp; trackers blocking?&lt;/h2&gt;\n&lt;p&gt;Can you make a guess how serving ads in app/websites work? they are from a server too, that server has a domain linked to it.. can you see where I am are going with this?&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;good content -&amp;gt; respond with ip address\nad/tracker -&amp;gt; respond with 0.0.0.0\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Not just blocking ads &amp;amp; tracking, you can do much more.. there are other things that you can do in dns level.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;block windows tracking or updates in windows machines&lt;/li&gt;\n&lt;li&gt;blocking spyware&lt;/li&gt;\n&lt;li&gt;disabling websites for a particular client (ex: blocking instagram on a your phone alone)&lt;/li&gt;\n&lt;li&gt;if you don&amp;rsquo;t want to keep tracking of your watch history in youtube\nand many more.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;aren-t-there-other-solutions-to-blocking-trackers-and-ads&#34;&gt;Aren&amp;rsquo;t there other solutions to blocking trackers and ads?&lt;/h3&gt;\n&lt;p&gt;yes but those come with a disadvantage,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Browser extensions.. only applies to browsers (system wide, everywhere and all applications)&lt;/li&gt;\n&lt;li&gt;Android, Iphone (separate apps that use vpn locally to achieve this) - Drains battery alot&lt;/li&gt;\n&lt;li&gt;Rooting android, Jailbreaking ios.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;so this does not require any of that.. no addons, no extra apps, no root, no jailbreak.&lt;/p&gt;\n&lt;h2 id=&#34;dns-over-tls&#34;&gt;DNS Over TLS&lt;/h2&gt;\n&lt;p&gt;Introduced in 2016, main idea is wrapping DNS requests and responses via TLS (Transport Layer Security), ensures observer will not be able to track or tamper the contents of the DNS requests.&lt;/p&gt;\n&lt;!-- ssl - secure socket layer --&gt;\n&lt;!-- tls - tranport layer security, offers end to end security. --&gt;\n&lt;p&gt;Shortly after, android introduced DOT as &amp;ldquo;Private DNS&amp;rdquo; on Android 9 &amp;amp; up, we will be using this feature in our devices to block all ADS &amp;amp; Trackers.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/DOT-noMITM.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;client -&amp;gt; dot server, dot server -&amp;gt; queries the dns.. (we forward our request to serveral dns servers, load-balancers)&lt;/p&gt;\n&lt;p&gt;Traditional DNS vs DOT&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/traditional-vs-dot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-do-we-do-it&#34;&gt;How do we do it?&lt;/h2&gt;\n&lt;p&gt;We use an existing open source dns resolver.&lt;/p&gt;\n&lt;p&gt;One options is to go with &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;PI Hole&lt;/a&gt;, it is a community favourite open source project that has all the bells and whistles for blocking ads and trackers integrated inside it.&lt;/p&gt;\n&lt;p&gt;We will be using &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AG Home&lt;/a&gt;, it is also a popular open source project, which is community maintained. up until lastmonth (dec 2022) I was using PI-hole, switched to AG-Home, just to try something new.&lt;/p&gt;\n&lt;h3 id=&#34;installing&#34;&gt;Installing&lt;/h3&gt;\n&lt;p&gt;I am assuming that you already have a linux box hosted in aws/google/..etc. DOT works on port 853, so you have to allow incoming and outgoing traffic in port 853.&lt;/p&gt;\n&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;\n&lt;p&gt;To make things easier, I will be using docker images to run the software.&lt;/p&gt;\n&lt;p&gt;running a docker container.. command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;\npodman run --name dnsServer\\\n--restart unless-stopped\\\n-p 2346:3000/tcp\\\n-p 1853:853/tcp\\\n-d adguard/adguardhome\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Things to note are, exposed port 3000 is the webport for admin page &amp;amp; 853 is for DOT.&lt;/p&gt;\n&lt;p&gt;We maintain the configs in a yaml file which will be mounted to the container.. here is an example version.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;bind_host: 0.0.0.0\nbind_port: 3000\nbeta_bind_port: 0\nusers:\n# - name: &amp;lt;your-username&amp;gt;\n# password: &amp;lt;your-passowrd&amp;gt;\nauth_attempts: 5\nblock_auth_min: 15\nhttp_proxy: &amp;quot;&amp;quot;\nlanguage: &amp;quot;&amp;quot;\ntheme: auto\ndebug_pprof: false\nweb_session_ttl: 720\ndns:\nbind_hosts:\n- 0.0.0.0\nblocked_response_ttl: 10\nratelimit: 20\nrefuse_any: true\nfastest_timeout: 1s\ntls:\nenabled: true\nserver_name: dns.bluepie.in\nport_https: 443\nport_dns_over_tls: 853\ncertificate_path: /opt/dnsServer/conf/fullchain.pem\nprivate_key_path: /opt/dnsServer/conf/privkey.pem\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once everything is configured correctly, the service will be up and running.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman ps\nb831cf04743d docker.io/adguard/adguardhome:latest --no-check-update... 1 days ago Up 3 weeks ago 0.0.0.0:1853-&amp;gt;853/tcp, 0.0.0.0:2346-&amp;gt;3000/tcp dnsServer\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;configuring-domain&#34;&gt;Configuring Domain&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;First we need a domain name, a paid one if you have it or a free one would be good, I&amp;rsquo;ll be creating a sub-domain from my domain, (i.e) &amp;lsquo;dns.bluepie.in&amp;rsquo; and get SSL certificates chain for the same.&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Inorder to uniquely identify clients, we need to create a wildcard sud-domain in our DNS provider, in my case godaddy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/creating-wild-card-dns.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h4 id=&#34;nginx&#34;&gt;Nginx&lt;/h4&gt;\n&lt;p&gt;We will be using nginx as a reverse proxy for exposing our services to the internet.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Admin Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server_name dns.bluepie.in;\nlocation / {\nproxy_pass http://localhost:2346/;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_redirect / / ;\nproxy_cookie_path / /;\nproxy_connect_timeout 15s;\nproxy_send_timeout 15s;\nproxy_read_timeout 15s;\n}\nlisten 443 ssl;\nssl_certificate /path/to/fullchain.pem;\nssl_certificate_key /path/to/privkey.pem;\ninclude /etc/ssl/options-ssl-nginx.conf;\nssl_dhparam /etc/ssl/ssl-dhparams.pem;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;DNS Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;nginx has a load balancing feature, stream directive to manage tcp and udp. we can use this to forward our requests to dns servers.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;stream {\nlog_format basic &#39;$time_iso8601 $remote_addr &#39;\n&#39;$protocol $status $bytes_sent $bytes_received &#39;\n&#39;$session_time $upstream_addr &#39;\n&#39;&amp;quot;$upstream_bytes_sent&amp;quot; &amp;quot;$upstream_bytes_received&amp;quot; &amp;quot;$upstream_connect_time&amp;quot;&#39;;\naccess_log /var/log/nginx/dns-access.log basic buffer=1k flush=5s;\nupstream dot {\nzone dot 512k;\nserver 127.0.0.1:1853;\n}\nserver {\nlisten 853;\nproxy_pass dot;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;admin-portal&#34;&gt;Admin portal&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;Stats page (configured to refresh every 24 hours)&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-statistics.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Configuring blocklists&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Over the years, I have accustomed to the following blocklists and it works best for me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-blocklists.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Blocklists (entires count taken at the time of writing this post&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# Blocks Ads, Mobile Ads, Phishing, Malvertising, Malware, Tracking, Telemetry, CryptoJacking, Analytics, Spyware, Ransomware, Exploit, Fraud, Abuse, Scam, Spam, Hijack, Misleading Marketing.\n# ~300,000 entries\nhttps://oisd.nl\n# ~180,000 entries\n# Extending and consolidating hosts files from several well-curated sources like adaway.org, mvps.org, malwaredomainlist.com, someonewhocares.org, and potentially others.\ngithub.com/StevenBlack/hosts\n# ~480,000 entries\n# Automatically updated, moderated and optimized list for blocking ads, trackers and other online garbage.\ngithub.com/notracking/hosts-blocklists\n# Specially Designed for Mobile Ad Protection.\n# ~200,000 entries\ngithub.com/jerryn70/GoodbyeAds\n# Blocking mobile ad providers and some analytics providers.\n# ~7,000 entries\ngithub.com/AdAway/adaway.github.io\n# A filter composed of several other filters (AdGuard Base filter, Social media filter, Tracking Protection filter, Mobile Ads filter, EasyList and EasyPrivacy) and simplified specifically to be better compatible with DNS-level ad blocking.\n# ~50,000 entries\ngithub.com/AdguardTeam/AdguardSDNSFilter\n# Protects you from many types of spyware, reduces bandwidth use, blocks certain pop-up traps, prevents user tracking by way of &amp;quot;web bugs&amp;quot; embedded in spam, provides partial protection to IE from certain web-based exploits and blocks most advertising you would otherwise be subjected to on the internet.\n# ~11,000 entries\nsomeonewhocares.org/hosts\n# Filter that blocks ads on mobile devices. Contains all known mobile ad networks.\n# ~1,000 entries\nkb.adguard.com/general/adguard-ad-filters#mobile-ads-filter\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-do-we-use-it&#34;&gt;How do we use it?&lt;/h2&gt;\n&lt;p&gt;Pick a unique word, ex: gopidevice1, then use this word as prefix for &amp;lsquo;dns.bluepie.in&amp;rsquo;. (i.e) &amp;lsquo;gopidevice1.dns.bluepie.in&amp;rsquo;, this is the url that you should use in your devices.&lt;/p&gt;\n&lt;h3 id=&#34;android&#34;&gt;Android&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/android-config.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;ios-iphone-ipad&#34;&gt;IOS - iphone, ipad&lt;/h3&gt;\n&lt;p&gt;Go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;, navigate to &amp;lsquo;tool&amp;rsquo;, give a name and use your unique url to create a profile.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;mac-windows&#34;&gt;Mac/Windows&lt;/h3&gt;\n&lt;p&gt;Same as iphone, go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dot-install-macos-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Migrating from PI-Hole to AGH was a good move, once you have used this and experienced &amp;ldquo;no ads&amp;rdquo;, you can not go back~&lt;/p&gt;</description></item></channel></rss>"}, "7820b62bba645b8c0e8c99a9055247f924d5358b": {"t": 1785770778.137897, "n": 169643, "h": "ad48649f0a3c4f3b", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sun, 27 Apr 2025 10:18:56 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Sub-Second Serverless Spring Boot/Reactor: Millisecond Cold-Starts via GraalVM</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I’m cutting down things over the past 15 days, focusing on items that I want to pursue. Thinking a lot… almost every day, making it a habit. My mind is too cluttered, and for reasons I’ll share in future posts. Future me… how is it going? Did everything go well? Did you make peace with your choice? When I was in hometown, a friend told his dad what’s going on with me since I seemed off when I visited him. I had a good conversation with him… maybe that is what I needed? He said something that made me think: explore your thoughts with friends/others—don’t keep it a secret. That’s how you get an idea/improve the idea; you will get the push you need. I feel this is true to some extent, but the other is also true. Again, future me, what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I’ve been playing Ghost of Tsushima. It helps me take my mind off things. The game is beautiful; I’m taking it slow. In more ways, it’s helping me relax and gives me a boost when I want to focus. I’m keeping it under control, though, not treating it like a time sink. When I was younger, I remember I used to lock in and play for days. Heck—the last game I got addicted to was Division 2. I used to plan and play raids with people, but I eventually lost touch with most of the guild members. Thinking back, my first game guild was from a game called Elsword. As everyone was getting busy with life and not logging into the game, we planned to disband the guild and had a last-day party with everyone. I have a video of that on my old hard drive. I was back from class and late to the party, and “Pandemonium,” the guild master, was waiting on me. We had a blast, and it was a nice sunset. Hopefully I can recover it and post it here… I had fond memories of that guild. Coming back to Division 2, I constructed a huge-ass essay on how it’s affecting my life, posted it to the guild and Discord servers, and left the game.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&amp;rsquo;rd Frost.) I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. Since I&amp;rsquo;m planning to vacate Chennai in March, after February (Frost&amp;rsquo;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can seamlessly automate the issuance and renewal of wildcard certificates. Gone are the days of logging into my DNS provider’s dashboard every few months. With minimal scripting and a few Cron entries, my wildcard certificates can renew without me lifting a finger.&lt;/p&gt;\n&lt;!-- If this guide was helpful, [check out more of my tech insights at BluePie](https://bluepie.in) or leave a comment below to share your experience or ask questions! --&gt;\n&lt;p&gt;Leave a comment below to share your experience or ask questions!&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;p&gt;See you all soon!&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item><item><title>Updating Dns Servers for Ads and Tracking Protection in 2023</title><link>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</link><pubDate>Sat, 28 Jan 2023 06:02:22 +0000</pubDate><guid>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</guid><description>\n&lt;h2 id=&#34;upgrading-pi-hole&#34;&gt;Upgrading PI hole&lt;/h2&gt;\n&lt;p&gt;Once every year, I do server maintainence (during Dec-Jan), this year I updated the our dns server from &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;pi-hole&lt;/a&gt; to &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AGH&lt;/a&gt; (both are open source projects..) Just wanted to try an alternative, after using it for a while, the UI &amp;amp; Features on AGH is much better than pi-hole.&lt;/p&gt;\n&lt;h2 id=&#34;why-a-custom-dns-server&#34;&gt;Why a custom dns server?&lt;/h2&gt;\n&lt;p&gt;All websites/apis are just ip addresses (just numbers). Nameservers maintains a mapping of the domain names to the ip addresses, so when we make a request, we get the appropriate ip address.&lt;/p&gt;\n&lt;p&gt;The idea is simple, by running our own dns server, we can prevent our network providers spying on us, on top of that we can intercept &amp;amp; block all ads &amp;amp; trackers.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;I get ads, but what are Trackers?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Similar to ads, ever been in a situation where you searched for something in flipkart/amazon.. then later you find the ad to buy the product elsewhere, this is because of trackers.. trackers are worse because they collect your data, the same way we block ads, trackers should be blocked.&lt;/p&gt;\n&lt;h2 id=&#34;how-this-can-help-us-with-ad-trackers-blocking&#34;&gt;How this can help us with ad &amp;amp; trackers blocking?&lt;/h2&gt;\n&lt;p&gt;Can you make a guess how serving ads in app/websites work? they are from a server too, that server has a domain linked to it.. can you see where I am are going with this?&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;good content -&amp;gt; respond with ip address\nad/tracker -&amp;gt; respond with 0.0.0.0\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Not just blocking ads &amp;amp; tracking, you can do much more.. there are other things that you can do in dns level.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;block windows tracking or updates in windows machines&lt;/li&gt;\n&lt;li&gt;blocking spyware&lt;/li&gt;\n&lt;li&gt;disabling websites for a particular client (ex: blocking instagram on a your phone alone)&lt;/li&gt;\n&lt;li&gt;if you don&amp;rsquo;t want to keep tracking of your watch history in youtube\nand many more.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;aren-t-there-other-solutions-to-blocking-trackers-and-ads&#34;&gt;Aren&amp;rsquo;t there other solutions to blocking trackers and ads?&lt;/h3&gt;\n&lt;p&gt;yes but those come with a disadvantage,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Browser extensions.. only applies to browsers (system wide, everywhere and all applications)&lt;/li&gt;\n&lt;li&gt;Android, Iphone (separate apps that use vpn locally to achieve this) - Drains battery alot&lt;/li&gt;\n&lt;li&gt;Rooting android, Jailbreaking ios.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;so this does not require any of that.. no addons, no extra apps, no root, no jailbreak.&lt;/p&gt;\n&lt;h2 id=&#34;dns-over-tls&#34;&gt;DNS Over TLS&lt;/h2&gt;\n&lt;p&gt;Introduced in 2016, main idea is wrapping DNS requests and responses via TLS (Transport Layer Security), ensures observer will not be able to track or tamper the contents of the DNS requests.&lt;/p&gt;\n&lt;!-- ssl - secure socket layer --&gt;\n&lt;!-- tls - tranport layer security, offers end to end security. --&gt;\n&lt;p&gt;Shortly after, android introduced DOT as &amp;ldquo;Private DNS&amp;rdquo; on Android 9 &amp;amp; up, we will be using this feature in our devices to block all ADS &amp;amp; Trackers.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/DOT-noMITM.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;client -&amp;gt; dot server, dot server -&amp;gt; queries the dns.. (we forward our request to serveral dns servers, load-balancers)&lt;/p&gt;\n&lt;p&gt;Traditional DNS vs DOT&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/traditional-vs-dot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-do-we-do-it&#34;&gt;How do we do it?&lt;/h2&gt;\n&lt;p&gt;We use an existing open source dns resolver.&lt;/p&gt;\n&lt;p&gt;One options is to go with &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;PI Hole&lt;/a&gt;, it is a community favourite open source project that has all the bells and whistles for blocking ads and trackers integrated inside it.&lt;/p&gt;\n&lt;p&gt;We will be using &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AG Home&lt;/a&gt;, it is also a popular open source project, which is community maintained. up until lastmonth (dec 2022) I was using PI-hole, switched to AG-Home, just to try something new.&lt;/p&gt;\n&lt;h3 id=&#34;installing&#34;&gt;Installing&lt;/h3&gt;\n&lt;p&gt;I am assuming that you already have a linux box hosted in aws/google/..etc. DOT works on port 853, so you have to allow incoming and outgoing traffic in port 853.&lt;/p&gt;\n&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;\n&lt;p&gt;To make things easier, I will be using docker images to run the software.&lt;/p&gt;\n&lt;p&gt;running a docker container.. command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;\npodman run --name dnsServer\\\n--restart unless-stopped\\\n-p 2346:3000/tcp\\\n-p 1853:853/tcp\\\n-d adguard/adguardhome\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Things to note are, exposed port 3000 is the webport for admin page &amp;amp; 853 is for DOT.&lt;/p&gt;\n&lt;p&gt;We maintain the configs in a yaml file which will be mounted to the container.. here is an example version.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;bind_host: 0.0.0.0\nbind_port: 3000\nbeta_bind_port: 0\nusers:\n# - name: &amp;lt;your-username&amp;gt;\n# password: &amp;lt;your-passowrd&amp;gt;\nauth_attempts: 5\nblock_auth_min: 15\nhttp_proxy: &amp;quot;&amp;quot;\nlanguage: &amp;quot;&amp;quot;\ntheme: auto\ndebug_pprof: false\nweb_session_ttl: 720\ndns:\nbind_hosts:\n- 0.0.0.0\nblocked_response_ttl: 10\nratelimit: 20\nrefuse_any: true\nfastest_timeout: 1s\ntls:\nenabled: true\nserver_name: dns.bluepie.in\nport_https: 443\nport_dns_over_tls: 853\ncertificate_path: /opt/dnsServer/conf/fullchain.pem\nprivate_key_path: /opt/dnsServer/conf/privkey.pem\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once everything is configured correctly, the service will be up and running.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman ps\nb831cf04743d docker.io/adguard/adguardhome:latest --no-check-update... 1 days ago Up 3 weeks ago 0.0.0.0:1853-&amp;gt;853/tcp, 0.0.0.0:2346-&amp;gt;3000/tcp dnsServer\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;configuring-domain&#34;&gt;Configuring Domain&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;First we need a domain name, a paid one if you have it or a free one would be good, I&amp;rsquo;ll be creating a sub-domain from my domain, (i.e) &amp;lsquo;dns.bluepie.in&amp;rsquo; and get SSL certificates chain for the same.&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Inorder to uniquely identify clients, we need to create a wildcard sud-domain in our DNS provider, in my case godaddy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/creating-wild-card-dns.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h4 id=&#34;nginx&#34;&gt;Nginx&lt;/h4&gt;\n&lt;p&gt;We will be using nginx as a reverse proxy for exposing our services to the internet.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Admin Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server_name dns.bluepie.in;\nlocation / {\nproxy_pass http://localhost:2346/;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_redirect / / ;\nproxy_cookie_path / /;\nproxy_connect_timeout 15s;\nproxy_send_timeout 15s;\nproxy_read_timeout 15s;\n}\nlisten 443 ssl;\nssl_certificate /path/to/fullchain.pem;\nssl_certificate_key /path/to/privkey.pem;\ninclude /etc/ssl/options-ssl-nginx.conf;\nssl_dhparam /etc/ssl/ssl-dhparams.pem;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;DNS Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;nginx has a load balancing feature, stream directive to manage tcp and udp. we can use this to forward our requests to dns servers.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;stream {\nlog_format basic &#39;$time_iso8601 $remote_addr &#39;\n&#39;$protocol $status $bytes_sent $bytes_received &#39;\n&#39;$session_time $upstream_addr &#39;\n&#39;&amp;quot;$upstream_bytes_sent&amp;quot; &amp;quot;$upstream_bytes_received&amp;quot; &amp;quot;$upstream_connect_time&amp;quot;&#39;;\naccess_log /var/log/nginx/dns-access.log basic buffer=1k flush=5s;\nupstream dot {\nzone dot 512k;\nserver 127.0.0.1:1853;\n}\nserver {\nlisten 853;\nproxy_pass dot;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;admin-portal&#34;&gt;Admin portal&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;Stats page (configured to refresh every 24 hours)&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-statistics.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Configuring blocklists&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Over the years, I have accustomed to the following blocklists and it works best for me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-blocklists.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Blocklists (entires count taken at the time of writing this post&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# Blocks Ads, Mobile Ads, Phishing, Malvertising, Malware, Tracking, Telemetry, CryptoJacking, Analytics, Spyware, Ransomware, Exploit, Fraud, Abuse, Scam, Spam, Hijack, Misleading Marketing.\n# ~300,000 entries\nhttps://oisd.nl\n# ~180,000 entries\n# Extending and consolidating hosts files from several well-curated sources like adaway.org, mvps.org, malwaredomainlist.com, someonewhocares.org, and potentially others.\ngithub.com/StevenBlack/hosts\n# ~480,000 entries\n# Automatically updated, moderated and optimized list for blocking ads, trackers and other online garbage.\ngithub.com/notracking/hosts-blocklists\n# Specially Designed for Mobile Ad Protection.\n# ~200,000 entries\ngithub.com/jerryn70/GoodbyeAds\n# Blocking mobile ad providers and some analytics providers.\n# ~7,000 entries\ngithub.com/AdAway/adaway.github.io\n# A filter composed of several other filters (AdGuard Base filter, Social media filter, Tracking Protection filter, Mobile Ads filter, EasyList and EasyPrivacy) and simplified specifically to be better compatible with DNS-level ad blocking.\n# ~50,000 entries\ngithub.com/AdguardTeam/AdguardSDNSFilter\n# Protects you from many types of spyware, reduces bandwidth use, blocks certain pop-up traps, prevents user tracking by way of &amp;quot;web bugs&amp;quot; embedded in spam, provides partial protection to IE from certain web-based exploits and blocks most advertising you would otherwise be subjected to on the internet.\n# ~11,000 entries\nsomeonewhocares.org/hosts\n# Filter that blocks ads on mobile devices. Contains all known mobile ad networks.\n# ~1,000 entries\nkb.adguard.com/general/adguard-ad-filters#mobile-ads-filter\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-do-we-use-it&#34;&gt;How do we use it?&lt;/h2&gt;\n&lt;p&gt;Pick a unique word, ex: gopidevice1, then use this word as prefix for &amp;lsquo;dns.bluepie.in&amp;rsquo;. (i.e) &amp;lsquo;gopidevice1.dns.bluepie.in&amp;rsquo;, this is the url that you should use in your devices.&lt;/p&gt;\n&lt;h3 id=&#34;android&#34;&gt;Android&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/android-config.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;ios-iphone-ipad&#34;&gt;IOS - iphone, ipad&lt;/h3&gt;\n&lt;p&gt;Go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;, navigate to &amp;lsquo;tool&amp;rsquo;, give a name and use your unique url to create a profile.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;mac-windows&#34;&gt;Mac/Windows&lt;/h3&gt;\n&lt;p&gt;Same as iphone, go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dot-install-macos-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Migrating from PI-Hole to AGH was a good move, once you have used this and experienced &amp;ldquo;no ads&amp;rdquo;, you can not go back~&lt;/p&gt;</description></item></channel></rss>"}, "876cebd2fee5a538d39eec6814e40a3bf9ce287b": {"t": 1785770781.0628815, "n": 169877, "h": "3393a0700f39e098", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sun, 27 Apr 2025 10:18:56 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Reducing Spring Reactor Java Project Cold Start to Milliseconds</title><link>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Sun, 27 Apr 2025 10:18:56 +0000</pubDate><guid>//bluepie.in/2025/04/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;Below you’ll find the why-and-how, plus comparisons with Quarkus &amp;amp; Micronaut.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle.kts snippet\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m cutting down things over the past 15 days, focusing on items that I want to puruse.. Thinking a lot.. almost everyday making it a habbit. My mind is too much cluttered and for reasons..i&amp;rsquo;ll share in future posts, future me.. how its going? did everything go well? &lt;del&gt;did you make the right choise?&lt;/del&gt; did you make peace with your choise? When I was in hometonw, frined told his dad whats going on with me since I seemed off when I visited him, had a good conversation with him.. maybe that is what I needed..? He said something that made me think.. explore your thoughts with friends/ others don&amp;rsquo;t keep it a secret.. thats how you get an idea/ improve the idea, you will get the push you need?.. I feel this is true to some extent but the other is also true.. Again future me what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I&amp;rsquo;ve been playing Ghost of Tsushima, It helps me take my mind off things. The game is beautiful, taking it slow. In more ways its helping me relax and gives me a boost when I want to focus. I&amp;rsquo;m keeping it under control though, not treating it like a time sink, When I was younger, I remember.. I used to lock in and play for days, heck! the last game I got addicted on, division 2. Used to plan and play raids with people, lost touch with most people from guilds, thinking back.. my first game guild will be on from a game called Elsword.. As everyone was getting busy with life and not logging into game, we planned to disband the guild and had a last day party with everyone. we have a video of that in my old harddrive, I was back from class and I was late to the party and &amp;ldquo;Pandemonim&amp;rdquo; the guild master was waiting on me and we had a blast and it was a nice sunset.. Hopefully I can recover it and post it here.. I had fond memories from that guild. Coming back to Division 2, I constructed a huge ass essay on how its affecting my life posted it to the guild and discord servers and left it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&amp;rsquo;rd Frost.) I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. Since I&amp;rsquo;m planning to vacate Chennai in March, after February (Frost&amp;rsquo;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can seamlessly automate the issuance and renewal of wildcard certificates. Gone are the days of logging into my DNS provider’s dashboard every few months. With minimal scripting and a few Cron entries, my wildcard certificates can renew without me lifting a finger.&lt;/p&gt;\n&lt;!-- If this guide was helpful, [check out more of my tech insights at BluePie](https://bluepie.in) or leave a comment below to share your experience or ask questions! --&gt;\n&lt;p&gt;Leave a comment below to share your experience or ask questions!&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;p&gt;See you all soon!&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item><item><title>Updating Dns Servers for Ads and Tracking Protection in 2023</title><link>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</link><pubDate>Sat, 28 Jan 2023 06:02:22 +0000</pubDate><guid>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</guid><description>\n&lt;h2 id=&#34;upgrading-pi-hole&#34;&gt;Upgrading PI hole&lt;/h2&gt;\n&lt;p&gt;Once every year, I do server maintainence (during Dec-Jan), this year I updated the our dns server from &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;pi-hole&lt;/a&gt; to &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AGH&lt;/a&gt; (both are open source projects..) Just wanted to try an alternative, after using it for a while, the UI &amp;amp; Features on AGH is much better than pi-hole.&lt;/p&gt;\n&lt;h2 id=&#34;why-a-custom-dns-server&#34;&gt;Why a custom dns server?&lt;/h2&gt;\n&lt;p&gt;All websites/apis are just ip addresses (just numbers). Nameservers maintains a mapping of the domain names to the ip addresses, so when we make a request, we get the appropriate ip address.&lt;/p&gt;\n&lt;p&gt;The idea is simple, by running our own dns server, we can prevent our network providers spying on us, on top of that we can intercept &amp;amp; block all ads &amp;amp; trackers.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;I get ads, but what are Trackers?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Similar to ads, ever been in a situation where you searched for something in flipkart/amazon.. then later you find the ad to buy the product elsewhere, this is because of trackers.. trackers are worse because they collect your data, the same way we block ads, trackers should be blocked.&lt;/p&gt;\n&lt;h2 id=&#34;how-this-can-help-us-with-ad-trackers-blocking&#34;&gt;How this can help us with ad &amp;amp; trackers blocking?&lt;/h2&gt;\n&lt;p&gt;Can you make a guess how serving ads in app/websites work? they are from a server too, that server has a domain linked to it.. can you see where I am are going with this?&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;good content -&amp;gt; respond with ip address\nad/tracker -&amp;gt; respond with 0.0.0.0\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Not just blocking ads &amp;amp; tracking, you can do much more.. there are other things that you can do in dns level.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;block windows tracking or updates in windows machines&lt;/li&gt;\n&lt;li&gt;blocking spyware&lt;/li&gt;\n&lt;li&gt;disabling websites for a particular client (ex: blocking instagram on a your phone alone)&lt;/li&gt;\n&lt;li&gt;if you don&amp;rsquo;t want to keep tracking of your watch history in youtube\nand many more.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;aren-t-there-other-solutions-to-blocking-trackers-and-ads&#34;&gt;Aren&amp;rsquo;t there other solutions to blocking trackers and ads?&lt;/h3&gt;\n&lt;p&gt;yes but those come with a disadvantage,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Browser extensions.. only applies to browsers (system wide, everywhere and all applications)&lt;/li&gt;\n&lt;li&gt;Android, Iphone (separate apps that use vpn locally to achieve this) - Drains battery alot&lt;/li&gt;\n&lt;li&gt;Rooting android, Jailbreaking ios.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;so this does not require any of that.. no addons, no extra apps, no root, no jailbreak.&lt;/p&gt;\n&lt;h2 id=&#34;dns-over-tls&#34;&gt;DNS Over TLS&lt;/h2&gt;\n&lt;p&gt;Introduced in 2016, main idea is wrapping DNS requests and responses via TLS (Transport Layer Security), ensures observer will not be able to track or tamper the contents of the DNS requests.&lt;/p&gt;\n&lt;!-- ssl - secure socket layer --&gt;\n&lt;!-- tls - tranport layer security, offers end to end security. --&gt;\n&lt;p&gt;Shortly after, android introduced DOT as &amp;ldquo;Private DNS&amp;rdquo; on Android 9 &amp;amp; up, we will be using this feature in our devices to block all ADS &amp;amp; Trackers.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/DOT-noMITM.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;client -&amp;gt; dot server, dot server -&amp;gt; queries the dns.. (we forward our request to serveral dns servers, load-balancers)&lt;/p&gt;\n&lt;p&gt;Traditional DNS vs DOT&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/traditional-vs-dot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-do-we-do-it&#34;&gt;How do we do it?&lt;/h2&gt;\n&lt;p&gt;We use an existing open source dns resolver.&lt;/p&gt;\n&lt;p&gt;One options is to go with &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;PI Hole&lt;/a&gt;, it is a community favourite open source project that has all the bells and whistles for blocking ads and trackers integrated inside it.&lt;/p&gt;\n&lt;p&gt;We will be using &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AG Home&lt;/a&gt;, it is also a popular open source project, which is community maintained. up until lastmonth (dec 2022) I was using PI-hole, switched to AG-Home, just to try something new.&lt;/p&gt;\n&lt;h3 id=&#34;installing&#34;&gt;Installing&lt;/h3&gt;\n&lt;p&gt;I am assuming that you already have a linux box hosted in aws/google/..etc. DOT works on port 853, so you have to allow incoming and outgoing traffic in port 853.&lt;/p&gt;\n&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;\n&lt;p&gt;To make things easier, I will be using docker images to run the software.&lt;/p&gt;\n&lt;p&gt;running a docker container.. command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;\npodman run --name dnsServer\\\n--restart unless-stopped\\\n-p 2346:3000/tcp\\\n-p 1853:853/tcp\\\n-d adguard/adguardhome\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Things to note are, exposed port 3000 is the webport for admin page &amp;amp; 853 is for DOT.&lt;/p&gt;\n&lt;p&gt;We maintain the configs in a yaml file which will be mounted to the container.. here is an example version.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;bind_host: 0.0.0.0\nbind_port: 3000\nbeta_bind_port: 0\nusers:\n# - name: &amp;lt;your-username&amp;gt;\n# password: &amp;lt;your-passowrd&amp;gt;\nauth_attempts: 5\nblock_auth_min: 15\nhttp_proxy: &amp;quot;&amp;quot;\nlanguage: &amp;quot;&amp;quot;\ntheme: auto\ndebug_pprof: false\nweb_session_ttl: 720\ndns:\nbind_hosts:\n- 0.0.0.0\nblocked_response_ttl: 10\nratelimit: 20\nrefuse_any: true\nfastest_timeout: 1s\ntls:\nenabled: true\nserver_name: dns.bluepie.in\nport_https: 443\nport_dns_over_tls: 853\ncertificate_path: /opt/dnsServer/conf/fullchain.pem\nprivate_key_path: /opt/dnsServer/conf/privkey.pem\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once everything is configured correctly, the service will be up and running.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman ps\nb831cf04743d docker.io/adguard/adguardhome:latest --no-check-update... 1 days ago Up 3 weeks ago 0.0.0.0:1853-&amp;gt;853/tcp, 0.0.0.0:2346-&amp;gt;3000/tcp dnsServer\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;configuring-domain&#34;&gt;Configuring Domain&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;First we need a domain name, a paid one if you have it or a free one would be good, I&amp;rsquo;ll be creating a sub-domain from my domain, (i.e) &amp;lsquo;dns.bluepie.in&amp;rsquo; and get SSL certificates chain for the same.&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Inorder to uniquely identify clients, we need to create a wildcard sud-domain in our DNS provider, in my case godaddy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/creating-wild-card-dns.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h4 id=&#34;nginx&#34;&gt;Nginx&lt;/h4&gt;\n&lt;p&gt;We will be using nginx as a reverse proxy for exposing our services to the internet.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Admin Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server_name dns.bluepie.in;\nlocation / {\nproxy_pass http://localhost:2346/;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_redirect / / ;\nproxy_cookie_path / /;\nproxy_connect_timeout 15s;\nproxy_send_timeout 15s;\nproxy_read_timeout 15s;\n}\nlisten 443 ssl;\nssl_certificate /path/to/fullchain.pem;\nssl_certificate_key /path/to/privkey.pem;\ninclude /etc/ssl/options-ssl-nginx.conf;\nssl_dhparam /etc/ssl/ssl-dhparams.pem;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;DNS Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;nginx has a load balancing feature, stream directive to manage tcp and udp. we can use this to forward our requests to dns servers.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;stream {\nlog_format basic &#39;$time_iso8601 $remote_addr &#39;\n&#39;$protocol $status $bytes_sent $bytes_received &#39;\n&#39;$session_time $upstream_addr &#39;\n&#39;&amp;quot;$upstream_bytes_sent&amp;quot; &amp;quot;$upstream_bytes_received&amp;quot; &amp;quot;$upstream_connect_time&amp;quot;&#39;;\naccess_log /var/log/nginx/dns-access.log basic buffer=1k flush=5s;\nupstream dot {\nzone dot 512k;\nserver 127.0.0.1:1853;\n}\nserver {\nlisten 853;\nproxy_pass dot;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;admin-portal&#34;&gt;Admin portal&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;Stats page (configured to refresh every 24 hours)&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-statistics.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Configuring blocklists&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Over the years, I have accustomed to the following blocklists and it works best for me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-blocklists.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Blocklists (entires count taken at the time of writing this post&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# Blocks Ads, Mobile Ads, Phishing, Malvertising, Malware, Tracking, Telemetry, CryptoJacking, Analytics, Spyware, Ransomware, Exploit, Fraud, Abuse, Scam, Spam, Hijack, Misleading Marketing.\n# ~300,000 entries\nhttps://oisd.nl\n# ~180,000 entries\n# Extending and consolidating hosts files from several well-curated sources like adaway.org, mvps.org, malwaredomainlist.com, someonewhocares.org, and potentially others.\ngithub.com/StevenBlack/hosts\n# ~480,000 entries\n# Automatically updated, moderated and optimized list for blocking ads, trackers and other online garbage.\ngithub.com/notracking/hosts-blocklists\n# Specially Designed for Mobile Ad Protection.\n# ~200,000 entries\ngithub.com/jerryn70/GoodbyeAds\n# Blocking mobile ad providers and some analytics providers.\n# ~7,000 entries\ngithub.com/AdAway/adaway.github.io\n# A filter composed of several other filters (AdGuard Base filter, Social media filter, Tracking Protection filter, Mobile Ads filter, EasyList and EasyPrivacy) and simplified specifically to be better compatible with DNS-level ad blocking.\n# ~50,000 entries\ngithub.com/AdguardTeam/AdguardSDNSFilter\n# Protects you from many types of spyware, reduces bandwidth use, blocks certain pop-up traps, prevents user tracking by way of &amp;quot;web bugs&amp;quot; embedded in spam, provides partial protection to IE from certain web-based exploits and blocks most advertising you would otherwise be subjected to on the internet.\n# ~11,000 entries\nsomeonewhocares.org/hosts\n# Filter that blocks ads on mobile devices. Contains all known mobile ad networks.\n# ~1,000 entries\nkb.adguard.com/general/adguard-ad-filters#mobile-ads-filter\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-do-we-use-it&#34;&gt;How do we use it?&lt;/h2&gt;\n&lt;p&gt;Pick a unique word, ex: gopidevice1, then use this word as prefix for &amp;lsquo;dns.bluepie.in&amp;rsquo;. (i.e) &amp;lsquo;gopidevice1.dns.bluepie.in&amp;rsquo;, this is the url that you should use in your devices.&lt;/p&gt;\n&lt;h3 id=&#34;android&#34;&gt;Android&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/android-config.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;ios-iphone-ipad&#34;&gt;IOS - iphone, ipad&lt;/h3&gt;\n&lt;p&gt;Go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;, navigate to &amp;lsquo;tool&amp;rsquo;, give a name and use your unique url to create a profile.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;mac-windows&#34;&gt;Mac/Windows&lt;/h3&gt;\n&lt;p&gt;Same as iphone, go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dot-install-macos-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Migrating from PI-Hole to AGH was a good move, once you have used this and experienced &amp;ldquo;no ads&amp;rdquo;, you can not go back~&lt;/p&gt;</description></item></channel></rss>"}, "cd5976d1db06996c2557fa8522db3d0316727cb5": {"t": 1785770783.7355168, "n": 169877, "h": "aeb9d65ef5222848", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Mon, 31 Mar 2025 22:30:39 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>Reducing Spring Reactor Java Project Cold Start to Milliseconds</title><link>//bluepie.in/2025/03/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/reducing-spring-reactor-java-project-cold-start-to-milliseconds/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;I run several backend services built on different frameworks and languages, One of them being Spring Boot and Spring Reactor on my own Linux server. Recently, I asked myself: why not host these services on Google Cloud Run? Cloud Run offers a free tier for the first 2 million requests per month, but if I leave an instance running (with a minimum instance count), it costs around ₹200–₹500 monthly—something I&amp;rsquo;ve paid before. The problem is cold-start latency: a traditional Spring Boot/Reactor application can take over 15 seconds to start. My users will never wait that long. Once a Cloud Run instance starts, it stays alive for about 15 minutes of inactivity before shutting down. We can’t force users to endure a 15-second delay on first request.&lt;/p&gt;\n&lt;p&gt;TL;DR — A &lt;strong&gt;Spring WebFlux&lt;/strong&gt; app compiled with &lt;strong&gt;GraalVM native-image&lt;/strong&gt;:\n• cold-starts in ≈ 100 ms&lt;br /&gt;\n• uses ≈ 90 MB RSS&lt;br /&gt;\n• when deployed on Cloud Run behind Firebase Hosting, serves up to &lt;strong&gt;2 million requests/month for free&lt;/strong&gt;.&lt;/p&gt;\n&lt;p&gt;Below you’ll find the why-and-how, plus comparisons with Quarkus &amp;amp; Micronaut.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;why-care-about-cold-starts&#34;&gt;Why Care About Cold-Starts?&lt;/h2&gt;\n&lt;p&gt;“If my Spring service gets only 10 000 requests a month, do I really need to pay for a min-instance on Cloud Run?”&lt;/p&gt;\n&lt;p&gt;Answer: No. Even if each cold-start lasts 5 s, that’s only &lt;em&gt;125 vCPU-s&lt;/em&gt; and &lt;em&gt;250 GiB-s&lt;/em&gt;, which is well below the free monthly quotas of 180 k vCPU-s and 360 k GiB-s. But latency matters for user-facing APIs: a 3–5 s delay is acceptable for CRON jobs, not for UI interactions. Enter &lt;strong&gt;GraalVM Native Image&lt;/strong&gt;.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;spring-boot-3-native-the-easy-path&#34;&gt;Spring Boot 3 Native (the Easy Path)&lt;/h2&gt;\n&lt;p&gt;With Spring Boot 3.4.x and the GraalVM build tools plugin, creating a native image is nearly zero-effort:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# build.gradle.kts snippet\nplugins {\nid(&amp;quot;org.springframework.boot&amp;quot;) version &amp;quot;3.4.1&amp;quot;\nid(&amp;quot;org.graalvm.buildtools.native&amp;quot;) version &amp;quot;0.10.2&amp;quot;\n}\n# build and deploy\n./gradlew clean bootBuildImage -PBP_NATIVE_IMAGE=true \\\n--image-name=gcr.io/$PROJECT/webflux-native\ngcloud run deploy webflux-native \\\n--image gcr.io/$PROJECT/webflux-native \\\n--region us-central1 \\\n--platform managed \\\n--memory 512Mi \\\n--min-instances 0 \\\n--allow-unauthenticated\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;• Cold-start: 40–120 ms (0.25 vCPU)&lt;br /&gt;\n• Memory on Cloud Run: 70–90 MB&lt;br /&gt;\n• Code changes: none—your usual &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;Mono&lt;/code&gt;, &lt;code&gt;Flux&lt;/code&gt;, etc.&lt;/p&gt;\n&lt;p&gt;The Spring Boot plugin runs the Ahead-Of-Time (AOT) engine, generates reflection hints automatically, and hands them to GraalVM. No manual JSON is required.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;quarkus-vs-micronaut-vs-spring-boot-native&#34;&gt;Quarkus vs Micronaut vs Spring Boot Native&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Framework&lt;/th&gt;\n&lt;th&gt;Native Image Size&lt;/th&gt;\n&lt;th&gt;Cold-start (0.25 vCPU)&lt;/th&gt;\n&lt;th&gt;DI Model&lt;/th&gt;\n&lt;th&gt;Reactive API&lt;/th&gt;\n&lt;th&gt;Migration Effort&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;Spring Boot 3 Native&lt;/td&gt;\n&lt;td&gt;70–85 MB&lt;/td&gt;\n&lt;td&gt;40–120 ms&lt;/td&gt;\n&lt;td&gt;Spring DI&lt;/td&gt;\n&lt;td&gt;Reactor (Mono/Flux)&lt;/td&gt;\n&lt;td&gt;★☆☆☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Quarkus 3&lt;/td&gt;\n&lt;td&gt;45–60 MB&lt;/td&gt;\n&lt;td&gt;25–80 ms&lt;/td&gt;\n&lt;td&gt;CDI&lt;/td&gt;\n&lt;td&gt;Mutiny / Vert.x&lt;/td&gt;\n&lt;td&gt;★★★★☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;Micronaut 4&lt;/td&gt;\n&lt;td&gt;45–55 MB&lt;/td&gt;\n&lt;td&gt;20–60 ms&lt;/td&gt;\n&lt;td&gt;Compile-time DI&lt;/td&gt;\n&lt;td&gt;Reactor, RxJava&lt;/td&gt;\n&lt;td&gt;★★★☆☆&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Since my code already uses WebFlux, Spring Boot Native was a five-minute flip. The others are powerful but require annotation and API rewrites.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;cost-breakdown&#34;&gt;Cost Breakdown&lt;/h2&gt;\n&lt;p&gt;• Requests: first 2 million/month free (then \\$0.40/M)&lt;br /&gt;\n• CPU: first 180 000 vCPU-s/month free&lt;br /&gt;\n• Memory: first 360 000 GiB-s/month free&lt;br /&gt;\n• Idle min-instance: \\$0.0000025/vCPU-s + \\$0.0000025/GiB-s&lt;br /&gt;\n• Egress: first 1 GB free&lt;/p&gt;\n&lt;p&gt;Example:&lt;br /&gt;\n10 000 requests × 300 ms active ≈ 3 000 s total CPU time → ~63 000 free units → cost = \\$0.00.&lt;/p&gt;\n&lt;p&gt;Keeping one min-instance warm (0.25 vCPU / 512 MiB) costs ≈\\$3.50/month—still trivial compared to user-facing latency improvements.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;custom-domain-binding&#34;&gt;Custom Domain Binding&lt;/h2&gt;\n&lt;p&gt;I bound my service to a custom domain in Cloud Run—a new feature available in the &lt;code&gt;us-central1&lt;/code&gt; region. DNS setup was straightforward, and Cloud Run automatically provisions an SSL certificate.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;using-graalvm-for-my-existing-app&#34;&gt;Using GraalVM for My Existing App&lt;/h2&gt;\n&lt;p&gt;I already have a mature Spring WebFlux application, so the GraalVM native-image path is the simplest. I chose GraalVM Community Edition 21:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Install GraalVM JDK 21 and the &lt;code&gt;native-image&lt;/code&gt; component (or rely on Paketo buildpacks).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Add the &lt;code&gt;org.graalvm.buildtools.native&lt;/code&gt; plugin to &lt;code&gt;build.gradle&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Run:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./gradlew clean bootBuildImage \\\n-Pprofile=cloudrun \\\n--no-daemon\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Deploy with:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcloud run deploy my-service \\\n--image gcr.io/$PROJECT/my-service:latest \\\n--region us-central1 \\\n--allow-unauthenticated \\\n--min-instances=0\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;hiccup-runtime-hints-for-reflection&#34;&gt;Hiccup: Runtime Hints for Reflection&lt;/h3&gt;\n&lt;p&gt;GraalVM performs aggressive tree-shaking and needs explicit hints for any reflection usage. In my case, I use &lt;code&gt;jjwt&lt;/code&gt; for JWT handling. Without hints, the native binary would fail at runtime, although development mode wouldn’t show errors. I provided a small registrar:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class JjwtRuntimeHints implements RuntimeHintsRegistrar {\nprivate static final Class&amp;lt;?&amp;gt;[] JJWT_TYPES = {\nDefaultJwtParser.class,\nDefaultClaimsBuilder.class,\nStandardSecureDigestAlgorithms.class,\nStandardKeyOperations.class,\nSignatureAlgorithm.class,\nStandardEncryptionAlgorithms.class,\nStandardKeyAlgorithms.class,\nStandardCompressionAlgorithms.class,\nKeysBridge.class\n};\n@Override\npublic void registerHints(RuntimeHints hints, ClassLoader cl) {\nfor (Class&amp;lt;?&amp;gt; type : JJWT_TYPES) {\nhints.reflection().registerType(\ntype,\nMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,\nMemberCategory.INVOKE_PUBLIC_METHODS,\nMemberCategory.DECLARED_FIELDS\n);\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Just enough to satisfy &lt;code&gt;io.jsonwebtoken&lt;/code&gt; internals—they get scanned at native-image build time.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;final-artifact-sizes-and-cold-start&#34;&gt;Final Artifact Sizes and Cold-Start&lt;/h2&gt;\n&lt;p&gt;• Docker image: 109 MB raw&lt;br /&gt;\n• Compressed in registry: 76 MB&lt;br /&gt;\n• Cold-start observed: 0.603 ms (including initial PostgreSQL connection to a remote host)&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/artifactory-size.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april-2025/startuptime.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;ci-cd-github-actions-workflow&#34;&gt;CI/CD: GitHub Actions Workflow&lt;/h2&gt;\n&lt;p&gt;I automated build &amp;amp; deploy with a GitHub workflow on every push to &lt;code&gt;main&lt;/code&gt; branch, it will also work on pull request.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build and Deploy Backend to Cloud Run\non:\npush:\nbranches: [ main ]\npaths:\n- &#39;backend/**&#39;\n- &#39;.github/workflows/backend.yml&#39;\n....\n- name: Authenticate to GCP\nuses: google-github-actions/auth@v2\nwith:\ncredentials_json: ${{ secrets.GCP_SA_KEY }}\n- name: Activate Service Account\nrun: gcloud auth activate-service-account \\\n--key-file=${{ secrets.GCP_SA_KEY }}\n....\n- name: Set up GraalVM 21\nuses: graalvm/setup-graalvm@v1\nwith:\njava-version: &#39;21&#39;\ndistribution: &#39;graalvm-community&#39;\n....\n- name: Build Native Image Locally\nworking-directory: backend/\nrun: |\n./gradlew clean bootBuildImage \\\n-Pspring.profiles.active=cloudrun \\\n--imageName &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Push Versioned Image\nrun: docker push &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot;\n- name: Tag &amp;amp; Push Latest\nrun: |\ndocker tag &amp;quot;${{ steps.image_details.outputs.path }}:${{ steps.image_details.outputs.version }}&amp;quot; \\\n&amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\ndocker push &amp;quot;${{ steps.image_details.outputs.path }}:latest&amp;quot;\n- name: Deploy to Cloud Run\nuses: google-github-actions/deploy-cloudrun@v2\nwith:\nservice: opentasks-service\nregion: us-central1\n...\nproject_id: ${{ secrets.GCP_PROJECT_ID }}\nenv_vars: |\n...\nSPRING_R2DBC_URL=${{ secrets.R2DBC_URL }}\nSPRING_R2DBC_USERNAME=${{ secrets.R2DBC_USERNAME }}\nsecrets: |\n...\nSPRING_R2DBC_PASSWORD=db-pass:latest\nflags: |\n--memory=512Mi\n--min-instances=0\n--concurrency=250\n...\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Explanation of key parts:&lt;br /&gt;\n• Triggers on pushes to &lt;code&gt;main&lt;/code&gt; or manual dispatch.&lt;br /&gt;\n• Authenticates with a GCP service account.&lt;br /&gt;\n• Sets up GraalVM 21 community edition.&lt;br /&gt;\n• Build the native image locally via &lt;code&gt;bootBuildImage&lt;/code&gt;.&lt;br /&gt;\n• Pushes both versioned and &lt;code&gt;latest&lt;/code&gt; tags to Artifact Registry.&lt;br /&gt;\n• Deploys to Cloud Run with zero minimum instances, custom environment variables, and resource flags.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;By combining Spring Boot 3’s native-image support with GraalVM, I reduced my service’s cold-start time from 15 seconds to under 1 second, fitting in under 100 MB of memory. This lets me use Cloud Run’s free tier without sacrificing user experience.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Spring Boot Native Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.graalvm.org/latest/reference-manual/native-image/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GraalVM Native Image Guide&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/run/pricing&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Cloud Run Free Tier &amp;amp; Pricing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;m cutting down things over the past 15 days, focusing on items that I want to puruse.. Thinking a lot.. almost everyday making it a habbit. My mind is too much cluttered and for reasons..i&amp;rsquo;ll share in future posts, future me.. how its going? did everything go well? &lt;del&gt;did you make the right choise?&lt;/del&gt; did you make peace with your choise? When I was in hometonw, frined told his dad whats going on with me since I seemed off when I visited him, had a good conversation with him.. maybe that is what I needed..? He said something that made me think.. explore your thoughts with friends/ others don&amp;rsquo;t keep it a secret.. thats how you get an idea/ improve the idea, you will get the push you need?.. I feel this is true to some extent but the other is also true.. Again future me what do you think?&lt;/p&gt;\n&lt;p&gt;In some of my free time, I&amp;rsquo;ve been playing Ghost of Tsushima, It helps me take my mind off things. The game is beautiful, taking it slow. In more ways its helping me relax and gives me a boost when I want to focus. I&amp;rsquo;m keeping it under control though, not treating it like a time sink, When I was younger, I remember.. I used to lock in and play for days, heck! the last game I got addicted on, division 2. Used to plan and play raids with people, lost touch with most people from guilds, thinking back.. my first game guild will be on from a game called Elsword.. As everyone was getting busy with life and not logging into game, we planned to disband the guild and had a last day party with everyone. we have a video of that in my old harddrive, I was back from class and I was late to the party and &amp;ldquo;Pandemonim&amp;rdquo; the guild master was waiting on me and we had a blast and it was a nice sunset.. Hopefully I can recover it and post it here.. I had fond memories from that guild. Coming back to Division 2, I constructed a huge ass essay on how its affecting my life posted it to the guild and discord servers and left it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april-2025/Ghost-Of-Tsushima-april-2025.mp4&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;</description></item><item><title>JWT Authentication with Refresh Tokens in Spring WebFlux</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&amp;rsquo;rd Frost.) I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. Since I&amp;rsquo;m planning to vacate Chennai in March, after February (Frost&amp;rsquo;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can seamlessly automate the issuance and renewal of wildcard certificates. Gone are the days of logging into my DNS provider’s dashboard every few months. With minimal scripting and a few Cron entries, my wildcard certificates can renew without me lifting a finger.&lt;/p&gt;\n&lt;!-- If this guide was helpful, [check out more of my tech insights at BluePie](https://bluepie.in) or leave a comment below to share your experience or ask questions! --&gt;\n&lt;p&gt;Leave a comment below to share your experience or ask questions!&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;p&gt;See you all soon!&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item><item><title>Updating Dns Servers for Ads and Tracking Protection in 2023</title><link>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</link><pubDate>Sat, 28 Jan 2023 06:02:22 +0000</pubDate><guid>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</guid><description>\n&lt;h2 id=&#34;upgrading-pi-hole&#34;&gt;Upgrading PI hole&lt;/h2&gt;\n&lt;p&gt;Once every year, I do server maintainence (during Dec-Jan), this year I updated the our dns server from &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;pi-hole&lt;/a&gt; to &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AGH&lt;/a&gt; (both are open source projects..) Just wanted to try an alternative, after using it for a while, the UI &amp;amp; Features on AGH is much better than pi-hole.&lt;/p&gt;\n&lt;h2 id=&#34;why-a-custom-dns-server&#34;&gt;Why a custom dns server?&lt;/h2&gt;\n&lt;p&gt;All websites/apis are just ip addresses (just numbers). Nameservers maintains a mapping of the domain names to the ip addresses, so when we make a request, we get the appropriate ip address.&lt;/p&gt;\n&lt;p&gt;The idea is simple, by running our own dns server, we can prevent our network providers spying on us, on top of that we can intercept &amp;amp; block all ads &amp;amp; trackers.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;I get ads, but what are Trackers?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Similar to ads, ever been in a situation where you searched for something in flipkart/amazon.. then later you find the ad to buy the product elsewhere, this is because of trackers.. trackers are worse because they collect your data, the same way we block ads, trackers should be blocked.&lt;/p&gt;\n&lt;h2 id=&#34;how-this-can-help-us-with-ad-trackers-blocking&#34;&gt;How this can help us with ad &amp;amp; trackers blocking?&lt;/h2&gt;\n&lt;p&gt;Can you make a guess how serving ads in app/websites work? they are from a server too, that server has a domain linked to it.. can you see where I am are going with this?&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;good content -&amp;gt; respond with ip address\nad/tracker -&amp;gt; respond with 0.0.0.0\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Not just blocking ads &amp;amp; tracking, you can do much more.. there are other things that you can do in dns level.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;block windows tracking or updates in windows machines&lt;/li&gt;\n&lt;li&gt;blocking spyware&lt;/li&gt;\n&lt;li&gt;disabling websites for a particular client (ex: blocking instagram on a your phone alone)&lt;/li&gt;\n&lt;li&gt;if you don&amp;rsquo;t want to keep tracking of your watch history in youtube\nand many more.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;aren-t-there-other-solutions-to-blocking-trackers-and-ads&#34;&gt;Aren&amp;rsquo;t there other solutions to blocking trackers and ads?&lt;/h3&gt;\n&lt;p&gt;yes but those come with a disadvantage,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Browser extensions.. only applies to browsers (system wide, everywhere and all applications)&lt;/li&gt;\n&lt;li&gt;Android, Iphone (separate apps that use vpn locally to achieve this) - Drains battery alot&lt;/li&gt;\n&lt;li&gt;Rooting android, Jailbreaking ios.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;so this does not require any of that.. no addons, no extra apps, no root, no jailbreak.&lt;/p&gt;\n&lt;h2 id=&#34;dns-over-tls&#34;&gt;DNS Over TLS&lt;/h2&gt;\n&lt;p&gt;Introduced in 2016, main idea is wrapping DNS requests and responses via TLS (Transport Layer Security), ensures observer will not be able to track or tamper the contents of the DNS requests.&lt;/p&gt;\n&lt;!-- ssl - secure socket layer --&gt;\n&lt;!-- tls - tranport layer security, offers end to end security. --&gt;\n&lt;p&gt;Shortly after, android introduced DOT as &amp;ldquo;Private DNS&amp;rdquo; on Android 9 &amp;amp; up, we will be using this feature in our devices to block all ADS &amp;amp; Trackers.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/DOT-noMITM.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;client -&amp;gt; dot server, dot server -&amp;gt; queries the dns.. (we forward our request to serveral dns servers, load-balancers)&lt;/p&gt;\n&lt;p&gt;Traditional DNS vs DOT&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/traditional-vs-dot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-do-we-do-it&#34;&gt;How do we do it?&lt;/h2&gt;\n&lt;p&gt;We use an existing open source dns resolver.&lt;/p&gt;\n&lt;p&gt;One options is to go with &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;PI Hole&lt;/a&gt;, it is a community favourite open source project that has all the bells and whistles for blocking ads and trackers integrated inside it.&lt;/p&gt;\n&lt;p&gt;We will be using &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AG Home&lt;/a&gt;, it is also a popular open source project, which is community maintained. up until lastmonth (dec 2022) I was using PI-hole, switched to AG-Home, just to try something new.&lt;/p&gt;\n&lt;h3 id=&#34;installing&#34;&gt;Installing&lt;/h3&gt;\n&lt;p&gt;I am assuming that you already have a linux box hosted in aws/google/..etc. DOT works on port 853, so you have to allow incoming and outgoing traffic in port 853.&lt;/p&gt;\n&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;\n&lt;p&gt;To make things easier, I will be using docker images to run the software.&lt;/p&gt;\n&lt;p&gt;running a docker container.. command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;\npodman run --name dnsServer\\\n--restart unless-stopped\\\n-p 2346:3000/tcp\\\n-p 1853:853/tcp\\\n-d adguard/adguardhome\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Things to note are, exposed port 3000 is the webport for admin page &amp;amp; 853 is for DOT.&lt;/p&gt;\n&lt;p&gt;We maintain the configs in a yaml file which will be mounted to the container.. here is an example version.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;bind_host: 0.0.0.0\nbind_port: 3000\nbeta_bind_port: 0\nusers:\n# - name: &amp;lt;your-username&amp;gt;\n# password: &amp;lt;your-passowrd&amp;gt;\nauth_attempts: 5\nblock_auth_min: 15\nhttp_proxy: &amp;quot;&amp;quot;\nlanguage: &amp;quot;&amp;quot;\ntheme: auto\ndebug_pprof: false\nweb_session_ttl: 720\ndns:\nbind_hosts:\n- 0.0.0.0\nblocked_response_ttl: 10\nratelimit: 20\nrefuse_any: true\nfastest_timeout: 1s\ntls:\nenabled: true\nserver_name: dns.bluepie.in\nport_https: 443\nport_dns_over_tls: 853\ncertificate_path: /opt/dnsServer/conf/fullchain.pem\nprivate_key_path: /opt/dnsServer/conf/privkey.pem\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once everything is configured correctly, the service will be up and running.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman ps\nb831cf04743d docker.io/adguard/adguardhome:latest --no-check-update... 1 days ago Up 3 weeks ago 0.0.0.0:1853-&amp;gt;853/tcp, 0.0.0.0:2346-&amp;gt;3000/tcp dnsServer\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;configuring-domain&#34;&gt;Configuring Domain&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;First we need a domain name, a paid one if you have it or a free one would be good, I&amp;rsquo;ll be creating a sub-domain from my domain, (i.e) &amp;lsquo;dns.bluepie.in&amp;rsquo; and get SSL certificates chain for the same.&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Inorder to uniquely identify clients, we need to create a wildcard sud-domain in our DNS provider, in my case godaddy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/creating-wild-card-dns.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h4 id=&#34;nginx&#34;&gt;Nginx&lt;/h4&gt;\n&lt;p&gt;We will be using nginx as a reverse proxy for exposing our services to the internet.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Admin Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server_name dns.bluepie.in;\nlocation / {\nproxy_pass http://localhost:2346/;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_redirect / / ;\nproxy_cookie_path / /;\nproxy_connect_timeout 15s;\nproxy_send_timeout 15s;\nproxy_read_timeout 15s;\n}\nlisten 443 ssl;\nssl_certificate /path/to/fullchain.pem;\nssl_certificate_key /path/to/privkey.pem;\ninclude /etc/ssl/options-ssl-nginx.conf;\nssl_dhparam /etc/ssl/ssl-dhparams.pem;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;DNS Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;nginx has a load balancing feature, stream directive to manage tcp and udp. we can use this to forward our requests to dns servers.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;stream {\nlog_format basic &#39;$time_iso8601 $remote_addr &#39;\n&#39;$protocol $status $bytes_sent $bytes_received &#39;\n&#39;$session_time $upstream_addr &#39;\n&#39;&amp;quot;$upstream_bytes_sent&amp;quot; &amp;quot;$upstream_bytes_received&amp;quot; &amp;quot;$upstream_connect_time&amp;quot;&#39;;\naccess_log /var/log/nginx/dns-access.log basic buffer=1k flush=5s;\nupstream dot {\nzone dot 512k;\nserver 127.0.0.1:1853;\n}\nserver {\nlisten 853;\nproxy_pass dot;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;admin-portal&#34;&gt;Admin portal&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;Stats page (configured to refresh every 24 hours)&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-statistics.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Configuring blocklists&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Over the years, I have accustomed to the following blocklists and it works best for me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-blocklists.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Blocklists (entires count taken at the time of writing this post&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# Blocks Ads, Mobile Ads, Phishing, Malvertising, Malware, Tracking, Telemetry, CryptoJacking, Analytics, Spyware, Ransomware, Exploit, Fraud, Abuse, Scam, Spam, Hijack, Misleading Marketing.\n# ~300,000 entries\nhttps://oisd.nl\n# ~180,000 entries\n# Extending and consolidating hosts files from several well-curated sources like adaway.org, mvps.org, malwaredomainlist.com, someonewhocares.org, and potentially others.\ngithub.com/StevenBlack/hosts\n# ~480,000 entries\n# Automatically updated, moderated and optimized list for blocking ads, trackers and other online garbage.\ngithub.com/notracking/hosts-blocklists\n# Specially Designed for Mobile Ad Protection.\n# ~200,000 entries\ngithub.com/jerryn70/GoodbyeAds\n# Blocking mobile ad providers and some analytics providers.\n# ~7,000 entries\ngithub.com/AdAway/adaway.github.io\n# A filter composed of several other filters (AdGuard Base filter, Social media filter, Tracking Protection filter, Mobile Ads filter, EasyList and EasyPrivacy) and simplified specifically to be better compatible with DNS-level ad blocking.\n# ~50,000 entries\ngithub.com/AdguardTeam/AdguardSDNSFilter\n# Protects you from many types of spyware, reduces bandwidth use, blocks certain pop-up traps, prevents user tracking by way of &amp;quot;web bugs&amp;quot; embedded in spam, provides partial protection to IE from certain web-based exploits and blocks most advertising you would otherwise be subjected to on the internet.\n# ~11,000 entries\nsomeonewhocares.org/hosts\n# Filter that blocks ads on mobile devices. Contains all known mobile ad networks.\n# ~1,000 entries\nkb.adguard.com/general/adguard-ad-filters#mobile-ads-filter\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-do-we-use-it&#34;&gt;How do we use it?&lt;/h2&gt;\n&lt;p&gt;Pick a unique word, ex: gopidevice1, then use this word as prefix for &amp;lsquo;dns.bluepie.in&amp;rsquo;. (i.e) &amp;lsquo;gopidevice1.dns.bluepie.in&amp;rsquo;, this is the url that you should use in your devices.&lt;/p&gt;\n&lt;h3 id=&#34;android&#34;&gt;Android&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/android-config.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;ios-iphone-ipad&#34;&gt;IOS - iphone, ipad&lt;/h3&gt;\n&lt;p&gt;Go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;, navigate to &amp;lsquo;tool&amp;rsquo;, give a name and use your unique url to create a profile.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;mac-windows&#34;&gt;Mac/Windows&lt;/h3&gt;\n&lt;p&gt;Same as iphone, go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dot-install-macos-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Migrating from PI-Hole to AGH was a good move, once you have used this and experienced &amp;ldquo;no ads&amp;rdquo;, you can not go back~&lt;/p&gt;</description></item></channel></rss>"}, "f27f4ba8d622d9a543f33f570f1861ca6f378374": {"t": 1785770786.5078003, "n": 163986, "h": "ffecc03b86d155e3", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Mon, 31 Mar 2025 22:30:39 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (Gobi) is around ~600 ppm. In Chennai where I live, it’s around 800 ppm (metro water). Half the people get purified water for their drinking; there is business for those who supply water, while the other half get a water-purification system installed in their homes by a vendor/company that produces it. We can build a DIY homemade purifier. It’s a new challenge, a chance to learn new things, and cheaper than buying ready-made.&lt;/p&gt;\n&lt;p&gt;Water “hardness” is simply the concentration of dissolved calcium and magnesium ions in water. It is usually expressed in mg/L or ppm as CaCO₃.&lt;br /&gt;\n• Soft: 0–60 ppm&lt;br /&gt;\n• Moderately hard: 61–120 ppm&lt;br /&gt;\n• Hard: 121–180 ppm&lt;br /&gt;\n• Very hard: &amp;gt; 180 ppm&lt;br /&gt;\nHard water leaves scale in pipes and appliances and can affect taste, so reducing hardness is one of the main goals of an RO unit.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall-mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated-carbon pre-filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV-chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV-C sterilization of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input/output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineralizer (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two-side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre-filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet-valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off/isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post-carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate/housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X-clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General-purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding/labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash-memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings. We put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them up until the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;br /&gt;\n• The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;\n• Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane; we need to make sure the waste-water port is not the one we use.&lt;/em&gt;&lt;br /&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;&lt;br /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineralizer&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralized water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;i-also-got-a-ph-tester-measured-the-colour&#34;&gt;I also got a pH tester, measured the colour.&lt;/h2&gt;\n&lt;p&gt;The tester uses a liquid reagent: add 2 drops to 5 ml of water and compare the resulting colour to the chart in the pH tester.&lt;br /&gt;\n• Raw inlet water: pH ≈ 7.4 (greenish)&lt;br /&gt;\n• RO permeate before remineralizer: pH ≈ 6.3 (yellow)&lt;br /&gt;\n• Final water after remineralizer: pH ≈ 7.2 (light green)&lt;br /&gt;\nThis shows the remineralizer is doing its job—bringing the pH back close to neutral so the water is neither acidic nor alkaline.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with a zip-tie to a window in the kitchen. When everything is connected, leave it running for 20 mins; you can immediately detect the change. The TDS of the purified drinking-water bottle that we buy is about 25 ppm, I get 19 ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution Control Board India&lt;br /&gt;\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnant… even though there’s small progress. If you are working for big corpora, you are just a row in an Excel sheet. The feeling resonates more and more reading trending Hacker News and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies… in the increasing midst of responsibilities coming towards me. I also almost got into a marriage arranged by my dad, forced to go see them… the girl &amp;amp; girl’s family like me and are trying to convince me into marriage, my dad doing all his best to set this up, fighting with me, etc. The girl is good too, perfect for family, they are a distant relative, born in 2000… But there are other plans/God has other plans. ~future me reading this… what happened?&lt;/p&gt;\n&lt;p&gt;Talked to an old friend; he said he discovered a private office Wi-Fi password written on a note and has been using it secretly. That reminded me of the first time I cracked a WPA endpoint in 2015. It felt great back then. We had BSNL broadband—“unlimited” but stuck at 60 kbps. I used to combine my connection with my neighbor’s Wi-Fi for maximum speeds. Those were fun times; I even made a mod of an application that combined network connections&amp;hellip;Long story short, he reminded me of Wi-Fi hacking. When I got back home to Gobi, I brought out my old Wi-Fi packet-injecting dongle and Linux laptop to chennai. I found a couple of neighbors’ Wi-Fi networks. After shutting down my unreliable Airtel connection in Chennai, I switched to unlimited mobile data (from previous posts), so I decided to use their Wi-Fi (nobody uses wpa3.. even the new routers comes with wpa2, you can easily capture the handshake and crack it.. I used hashcat, its super fast and reliable). While on their network, I logged in to their router, checked their device list, and configured Wi-Fi settings for a longer range on 2.4 GHz by switching between 20 Hz and 40 Hz channels.. Some of the closest Wi-Fi access points are 5 GHz and offers low latency too.. By seeing the device list, I was able to mimic their device name and avoid being caught. For example: “smart-camera.”.. to be fair, I would just use them for internet..nothing more..nothing less.&lt;/p&gt;</description></item><item><title>JWT Authentication</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&amp;rsquo;rd Frost.) I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. Since I&amp;rsquo;m planning to vacate Chennai in March, after February (Frost&amp;rsquo;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can seamlessly automate the issuance and renewal of wildcard certificates. Gone are the days of logging into my DNS provider’s dashboard every few months. With minimal scripting and a few Cron entries, my wildcard certificates can renew without me lifting a finger.&lt;/p&gt;\n&lt;!-- If this guide was helpful, [check out more of my tech insights at BluePie](https://bluepie.in) or leave a comment below to share your experience or ask questions! --&gt;\n&lt;p&gt;Leave a comment below to share your experience or ask questions!&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;p&gt;See you all soon!&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item><item><title>Updating Dns Servers for Ads and Tracking Protection in 2023</title><link>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</link><pubDate>Sat, 28 Jan 2023 06:02:22 +0000</pubDate><guid>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</guid><description>\n&lt;h2 id=&#34;upgrading-pi-hole&#34;&gt;Upgrading PI hole&lt;/h2&gt;\n&lt;p&gt;Once every year, I do server maintainence (during Dec-Jan), this year I updated the our dns server from &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;pi-hole&lt;/a&gt; to &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AGH&lt;/a&gt; (both are open source projects..) Just wanted to try an alternative, after using it for a while, the UI &amp;amp; Features on AGH is much better than pi-hole.&lt;/p&gt;\n&lt;h2 id=&#34;why-a-custom-dns-server&#34;&gt;Why a custom dns server?&lt;/h2&gt;\n&lt;p&gt;All websites/apis are just ip addresses (just numbers). Nameservers maintains a mapping of the domain names to the ip addresses, so when we make a request, we get the appropriate ip address.&lt;/p&gt;\n&lt;p&gt;The idea is simple, by running our own dns server, we can prevent our network providers spying on us, on top of that we can intercept &amp;amp; block all ads &amp;amp; trackers.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;I get ads, but what are Trackers?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Similar to ads, ever been in a situation where you searched for something in flipkart/amazon.. then later you find the ad to buy the product elsewhere, this is because of trackers.. trackers are worse because they collect your data, the same way we block ads, trackers should be blocked.&lt;/p&gt;\n&lt;h2 id=&#34;how-this-can-help-us-with-ad-trackers-blocking&#34;&gt;How this can help us with ad &amp;amp; trackers blocking?&lt;/h2&gt;\n&lt;p&gt;Can you make a guess how serving ads in app/websites work? they are from a server too, that server has a domain linked to it.. can you see where I am are going with this?&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;good content -&amp;gt; respond with ip address\nad/tracker -&amp;gt; respond with 0.0.0.0\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Not just blocking ads &amp;amp; tracking, you can do much more.. there are other things that you can do in dns level.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;block windows tracking or updates in windows machines&lt;/li&gt;\n&lt;li&gt;blocking spyware&lt;/li&gt;\n&lt;li&gt;disabling websites for a particular client (ex: blocking instagram on a your phone alone)&lt;/li&gt;\n&lt;li&gt;if you don&amp;rsquo;t want to keep tracking of your watch history in youtube\nand many more.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;aren-t-there-other-solutions-to-blocking-trackers-and-ads&#34;&gt;Aren&amp;rsquo;t there other solutions to blocking trackers and ads?&lt;/h3&gt;\n&lt;p&gt;yes but those come with a disadvantage,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Browser extensions.. only applies to browsers (system wide, everywhere and all applications)&lt;/li&gt;\n&lt;li&gt;Android, Iphone (separate apps that use vpn locally to achieve this) - Drains battery alot&lt;/li&gt;\n&lt;li&gt;Rooting android, Jailbreaking ios.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;so this does not require any of that.. no addons, no extra apps, no root, no jailbreak.&lt;/p&gt;\n&lt;h2 id=&#34;dns-over-tls&#34;&gt;DNS Over TLS&lt;/h2&gt;\n&lt;p&gt;Introduced in 2016, main idea is wrapping DNS requests and responses via TLS (Transport Layer Security), ensures observer will not be able to track or tamper the contents of the DNS requests.&lt;/p&gt;\n&lt;!-- ssl - secure socket layer --&gt;\n&lt;!-- tls - tranport layer security, offers end to end security. --&gt;\n&lt;p&gt;Shortly after, android introduced DOT as &amp;ldquo;Private DNS&amp;rdquo; on Android 9 &amp;amp; up, we will be using this feature in our devices to block all ADS &amp;amp; Trackers.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/DOT-noMITM.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;client -&amp;gt; dot server, dot server -&amp;gt; queries the dns.. (we forward our request to serveral dns servers, load-balancers)&lt;/p&gt;\n&lt;p&gt;Traditional DNS vs DOT&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/traditional-vs-dot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-do-we-do-it&#34;&gt;How do we do it?&lt;/h2&gt;\n&lt;p&gt;We use an existing open source dns resolver.&lt;/p&gt;\n&lt;p&gt;One options is to go with &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;PI Hole&lt;/a&gt;, it is a community favourite open source project that has all the bells and whistles for blocking ads and trackers integrated inside it.&lt;/p&gt;\n&lt;p&gt;We will be using &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AG Home&lt;/a&gt;, it is also a popular open source project, which is community maintained. up until lastmonth (dec 2022) I was using PI-hole, switched to AG-Home, just to try something new.&lt;/p&gt;\n&lt;h3 id=&#34;installing&#34;&gt;Installing&lt;/h3&gt;\n&lt;p&gt;I am assuming that you already have a linux box hosted in aws/google/..etc. DOT works on port 853, so you have to allow incoming and outgoing traffic in port 853.&lt;/p&gt;\n&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;\n&lt;p&gt;To make things easier, I will be using docker images to run the software.&lt;/p&gt;\n&lt;p&gt;running a docker container.. command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;\npodman run --name dnsServer\\\n--restart unless-stopped\\\n-p 2346:3000/tcp\\\n-p 1853:853/tcp\\\n-d adguard/adguardhome\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Things to note are, exposed port 3000 is the webport for admin page &amp;amp; 853 is for DOT.&lt;/p&gt;\n&lt;p&gt;We maintain the configs in a yaml file which will be mounted to the container.. here is an example version.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;bind_host: 0.0.0.0\nbind_port: 3000\nbeta_bind_port: 0\nusers:\n# - name: &amp;lt;your-username&amp;gt;\n# password: &amp;lt;your-passowrd&amp;gt;\nauth_attempts: 5\nblock_auth_min: 15\nhttp_proxy: &amp;quot;&amp;quot;\nlanguage: &amp;quot;&amp;quot;\ntheme: auto\ndebug_pprof: false\nweb_session_ttl: 720\ndns:\nbind_hosts:\n- 0.0.0.0\nblocked_response_ttl: 10\nratelimit: 20\nrefuse_any: true\nfastest_timeout: 1s\ntls:\nenabled: true\nserver_name: dns.bluepie.in\nport_https: 443\nport_dns_over_tls: 853\ncertificate_path: /opt/dnsServer/conf/fullchain.pem\nprivate_key_path: /opt/dnsServer/conf/privkey.pem\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once everything is configured correctly, the service will be up and running.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman ps\nb831cf04743d docker.io/adguard/adguardhome:latest --no-check-update... 1 days ago Up 3 weeks ago 0.0.0.0:1853-&amp;gt;853/tcp, 0.0.0.0:2346-&amp;gt;3000/tcp dnsServer\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;configuring-domain&#34;&gt;Configuring Domain&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;First we need a domain name, a paid one if you have it or a free one would be good, I&amp;rsquo;ll be creating a sub-domain from my domain, (i.e) &amp;lsquo;dns.bluepie.in&amp;rsquo; and get SSL certificates chain for the same.&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Inorder to uniquely identify clients, we need to create a wildcard sud-domain in our DNS provider, in my case godaddy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/creating-wild-card-dns.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h4 id=&#34;nginx&#34;&gt;Nginx&lt;/h4&gt;\n&lt;p&gt;We will be using nginx as a reverse proxy for exposing our services to the internet.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Admin Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server_name dns.bluepie.in;\nlocation / {\nproxy_pass http://localhost:2346/;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_redirect / / ;\nproxy_cookie_path / /;\nproxy_connect_timeout 15s;\nproxy_send_timeout 15s;\nproxy_read_timeout 15s;\n}\nlisten 443 ssl;\nssl_certificate /path/to/fullchain.pem;\nssl_certificate_key /path/to/privkey.pem;\ninclude /etc/ssl/options-ssl-nginx.conf;\nssl_dhparam /etc/ssl/ssl-dhparams.pem;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;DNS Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;nginx has a load balancing feature, stream directive to manage tcp and udp. we can use this to forward our requests to dns servers.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;stream {\nlog_format basic &#39;$time_iso8601 $remote_addr &#39;\n&#39;$protocol $status $bytes_sent $bytes_received &#39;\n&#39;$session_time $upstream_addr &#39;\n&#39;&amp;quot;$upstream_bytes_sent&amp;quot; &amp;quot;$upstream_bytes_received&amp;quot; &amp;quot;$upstream_connect_time&amp;quot;&#39;;\naccess_log /var/log/nginx/dns-access.log basic buffer=1k flush=5s;\nupstream dot {\nzone dot 512k;\nserver 127.0.0.1:1853;\n}\nserver {\nlisten 853;\nproxy_pass dot;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;admin-portal&#34;&gt;Admin portal&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;Stats page (configured to refresh every 24 hours)&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-statistics.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Configuring blocklists&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Over the years, I have accustomed to the following blocklists and it works best for me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-blocklists.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Blocklists (entires count taken at the time of writing this post&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# Blocks Ads, Mobile Ads, Phishing, Malvertising, Malware, Tracking, Telemetry, CryptoJacking, Analytics, Spyware, Ransomware, Exploit, Fraud, Abuse, Scam, Spam, Hijack, Misleading Marketing.\n# ~300,000 entries\nhttps://oisd.nl\n# ~180,000 entries\n# Extending and consolidating hosts files from several well-curated sources like adaway.org, mvps.org, malwaredomainlist.com, someonewhocares.org, and potentially others.\ngithub.com/StevenBlack/hosts\n# ~480,000 entries\n# Automatically updated, moderated and optimized list for blocking ads, trackers and other online garbage.\ngithub.com/notracking/hosts-blocklists\n# Specially Designed for Mobile Ad Protection.\n# ~200,000 entries\ngithub.com/jerryn70/GoodbyeAds\n# Blocking mobile ad providers and some analytics providers.\n# ~7,000 entries\ngithub.com/AdAway/adaway.github.io\n# A filter composed of several other filters (AdGuard Base filter, Social media filter, Tracking Protection filter, Mobile Ads filter, EasyList and EasyPrivacy) and simplified specifically to be better compatible with DNS-level ad blocking.\n# ~50,000 entries\ngithub.com/AdguardTeam/AdguardSDNSFilter\n# Protects you from many types of spyware, reduces bandwidth use, blocks certain pop-up traps, prevents user tracking by way of &amp;quot;web bugs&amp;quot; embedded in spam, provides partial protection to IE from certain web-based exploits and blocks most advertising you would otherwise be subjected to on the internet.\n# ~11,000 entries\nsomeonewhocares.org/hosts\n# Filter that blocks ads on mobile devices. Contains all known mobile ad networks.\n# ~1,000 entries\nkb.adguard.com/general/adguard-ad-filters#mobile-ads-filter\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-do-we-use-it&#34;&gt;How do we use it?&lt;/h2&gt;\n&lt;p&gt;Pick a unique word, ex: gopidevice1, then use this word as prefix for &amp;lsquo;dns.bluepie.in&amp;rsquo;. (i.e) &amp;lsquo;gopidevice1.dns.bluepie.in&amp;rsquo;, this is the url that you should use in your devices.&lt;/p&gt;\n&lt;h3 id=&#34;android&#34;&gt;Android&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/android-config.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;ios-iphone-ipad&#34;&gt;IOS - iphone, ipad&lt;/h3&gt;\n&lt;p&gt;Go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;, navigate to &amp;lsquo;tool&amp;rsquo;, give a name and use your unique url to create a profile.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;mac-windows&#34;&gt;Mac/Windows&lt;/h3&gt;\n&lt;p&gt;Same as iphone, go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dot-install-macos-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Migrating from PI-Hole to AGH was a good move, once you have used this and experienced &amp;ldquo;no ads&amp;rdquo;, you can not go back~&lt;/p&gt;</description></item><item><title>Modding a macOS App</title><link>//bluepie.in/2022/02/modding-a-macos-app/</link><pubDate>Sun, 20 Feb 2022 19:10:13 +0000</pubDate><guid>//bluepie.in/2022/02/modding-a-macos-app/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;This post is about reversing.. but for a MACOS binary. Lets drive straight into it.&lt;/p&gt;\n&lt;p&gt;Modding in macOS is fairly new to me, but a binary is a binary and it can be modded!. I started to daily drive macOS a month or two ago, the why and what&amp;rsquo;s is a whole different story to post about.&lt;/p&gt;\n&lt;p&gt;Like I have said previously in my other &lt;a href=&#34;https://bluepie.in/categories/reversing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;re posts&lt;/a&gt;, &amp;ldquo; I don&amp;rsquo;t trust mods from the internet&amp;rdquo; If I can mod it then its fine, otherwise I&amp;rsquo;m paying for it.&lt;/p&gt;\n&lt;p&gt;I just started to understand the quirks of &amp;ldquo;OS X&amp;rdquo;, Though some of my methods may be crude&amp;hellip;it works, I will perfect them..&lt;/p&gt;\n&lt;h2 id=&#34;target&#34;&gt;Target&lt;/h2&gt;\n&lt;p&gt;So there is this great app for cleaning your Mac&amp;rsquo;s junk/storage files, but the free version only lets you clean 500mb daily.. the premium version is a whooping 3k per year&amp;hellip;do you get where I am going with this ? &amp;hellip;YES!&amp;hellip;modding for fun and profit (PROFIT = personal use)&lt;/p&gt;\n&lt;h2 id=&#34;tools&#34;&gt;Tools&lt;/h2&gt;\n&lt;p&gt;So I&amp;rsquo;m gonna be trying out ghidra &amp;ldquo; The NSA&amp;rsquo;s reverse engineering tool &amp;ldquo;, mainly because Its a open source tool and I wanted to do some reverse engineering with it for a long time now.&lt;/p&gt;\n&lt;p&gt;make sure you have &lt;a href=&#34;https://brew.sh/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;brew&lt;/a&gt; installed and then run the following command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-console&#34;&gt;brew install --cask ghidra\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;making-observation-to-the-target&#34;&gt;Making observation to the target&lt;/h2&gt;\n&lt;p&gt;The app throws you with the &amp;ldquo;Nag Screen&amp;rdquo; whenever you use the app, it blocks the usage of features (can not use the app to remove more than 500MB of data,privacy scanning won&amp;rsquo;t work unless you pay,..etc).&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;500mb storage limit reached &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/500mb-over-nag-screen.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Trial period expired &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/clean-my-mac-unmodded.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;basics-understanding-ghidra&#34;&gt;Basics &amp;amp; Understanding Ghidra&lt;/h2&gt;\n&lt;p&gt;All macOS apps have the following structure, the main binary will always be inside the MacOS directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/mac-app-folder-structure.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Launch Ghidra and create a new project, then drag and drop the binary into the project.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-new-project.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Mac has this binary format &amp;ldquo;macobinary&amp;rdquo; - basically it contains the different target executables, ex: (arm binary vs the x86 binary).. mine is a x86 machine, will be modding that alone.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-appropriate-target-binary.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;After the import, Ghidra will automatically ask you to analyse the target binary, choose Analyse. (it will make our life easier), based on the binary size and your cpu cores, it may take couple minutes.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/will-ask-for-analyse-choose-analyse.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Only focus on the following five windows, that is enough to get the job done.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/ghidra-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;1 = will contain the symbol tabel, this is where we do most of the filtering.&lt;/li&gt;\n&lt;li&gt;2 = shows the disassembly of the binary (assembly language), this is where we do the actual reversing, (i.e) patching, understanding logic, etc.&lt;/li&gt;\n&lt;li&gt;3 = This is a great feature, shows the equivalent c code, to the selected assembly section.&lt;/li&gt;\n&lt;li&gt;4 &amp;amp; 5 = shows the incoming and outgoing function calls, this will help us find interesting things about the binary, which then can be used for identifying logic and patching.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;IF ANY OF THE ABOVE WINDOWS ARE NOT OPEN, YOU CAN ENABLE THEM THROUGH THE MENU OPTION &amp;ldquo;WINDOW&amp;rdquo;.&lt;/p&gt;\n&lt;h2 id=&#34;analysis&#34;&gt;Analysis&lt;/h2&gt;\n&lt;p&gt;It will take some time to find what you are looking for, but I have already invested time in this, so what we need is &amp;ldquo;activationManager&amp;rdquo; class, it has all sorts of interesting functions like &amp;ldquo;activationStatus, isAppActivated..etc&amp;rdquo;. Looking for references, it is used throughout the app, so lets change it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/different-app-status.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Looking around, we find the different status codes inside &amp;ldquo;activationStatus&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;By checking other functions, it becomes obvious that, changing only &amp;ldquo;isAppActivated&amp;rdquo; function is enough. The return type of the function is &amp;ldquo;char&amp;rdquo;, with reference to &amp;ldquo;status&amp;rdquo; from above..we should return &amp;ldquo;3&amp;rdquo;. From the ascii table, &amp;ldquo;0x33&amp;rdquo; is the equivalnet for &amp;ldquo;3&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;Patching the &amp;ldquo;isAppActivated&amp;rdquo; function, with the following code.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;push rbp\nmov rbp,rsp\nmov DWORD PTR [rbp-0x4],0x0\nmov eax,0x33\npop rbp\nret\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then export the program &amp;ldquo;File -&amp;gt; Export Program&amp;rdquo;, our patched binary is ready. Now give executable permission and move it to the original directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/move-exported-binary-to-original-directory.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;rename the original&lt;/p&gt;\n&lt;h2 id=&#34;signing-running-the-modded-binary-for-profit&#34;&gt;Signing &amp;amp; Running the modded binary for PROFIT&lt;/h2&gt;\n&lt;p&gt;Unlike windows and linux, macOS won&amp;rsquo;t run applications without signing the binary.. but we can generate your own certificates for personal use (dont have to buy a developer account for 99$/year).&lt;/p&gt;\n&lt;p&gt;create a certificate using &amp;ldquo;keychain Access&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-custom-certifcate.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;choose &amp;ldquo;code signing&amp;rdquo; in &amp;ldquo;certificate type&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-code-signing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Go to the application root &amp;ldquo;/Application/&amp;rdquo; to resign the entire app.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo codesign -s &amp;quot;&amp;lt;name-of-certificate&amp;gt;&amp;quot; -fv CleanMyMac-MAS.app/\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Run the app&amp;hellip; Now its been modded and I don&amp;rsquo;t have to pay 3k INR a year or worry about untrusted binaries from the internet.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/removed-files-beyong-the-limit.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;You can see from the image, all the premium features are now unlocked &amp;amp; we can also remove the junk files exceeding 500MB.&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;This was a fun excerise to dig around. Macos is not that limited as I thought it was&amp;hellip; Sure it has its quirks.&lt;/p&gt;</description></item></channel></rss>"}, "b21b95ec5817dfd2850a729db5f6164a4d091e1b": {"t": 1785770791.0322843, "n": 161433, "h": "bb8f8a674d2e6c16", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Mon, 31 Mar 2025 22:30:39 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>DIY RO - Water Purification</title><link>//bluepie.in/2025/03/diy-water-purification/</link><pubDate>Mon, 31 Mar 2025 22:30:39 +0000</pubDate><guid>//bluepie.in/2025/03/diy-water-purification/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;The water hardness in my home (gobi) is around ~600ppm. In chennai where I live.. its around 800ppm (metro water), half get purificed water for their drinking, there is businees for these people who supply water for people, the other half get the water purficiation system in their home by a vendor/ company that produces it. We can build a DIY homemade purifier. Its a new challenge/ learning new things and cheaper than buying readymade.&lt;/p&gt;\n&lt;h2 id=&#34;components-needed&#34;&gt;Components needed&lt;/h2&gt;\n&lt;p&gt;Coming to things that are needed for the DIY setup, I need to buy the following items.&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;S.No&lt;/th&gt;\n&lt;th&gt;Item&lt;/th&gt;\n&lt;th&gt;Qty&lt;/th&gt;\n&lt;th&gt;Rate (₹)&lt;/th&gt;\n&lt;th&gt;Amount (₹)&lt;/th&gt;\n&lt;th&gt;Why it’s used&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;1&lt;/td&gt;\n&lt;td&gt;Wall‑mount plate (12″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;140.00&lt;/td&gt;\n&lt;td&gt;Base plate to fix the RO unit on the wall&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;2&lt;/td&gt;\n&lt;td&gt;BlueShell booster pump&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Raises pressure so the RO membrane works efficiently&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;3&lt;/td&gt;\n&lt;td&gt;BlueShell 100 GPD RO membrane&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;850.00&lt;/td&gt;\n&lt;td&gt;Main purification element that removes dissolved salts&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;4&lt;/td&gt;\n&lt;td&gt;GAC inline cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Granular activated‑carbon pre‑filter for chlorine removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;5&lt;/td&gt;\n&lt;td&gt;CTO 10″ carbon block&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;90.00&lt;/td&gt;\n&lt;td&gt;Carbon block for chlorine, taste, odour removal&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;6&lt;/td&gt;\n&lt;td&gt;Membrane housing (1812)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;130.00&lt;/td&gt;\n&lt;td&gt;Holds and seals the RO membrane&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;7&lt;/td&gt;\n&lt;td&gt;Accord 1.5 A SMPS&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;24 V DC power supply for pump &amp;amp; UV lamp&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;8&lt;/td&gt;\n&lt;td&gt;UV‑chamber set (11 W)&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;300.00&lt;/td&gt;\n&lt;td&gt;UV‑C sterilisation of product water after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;9&lt;/td&gt;\n&lt;td&gt;Handheld TDS meter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;450.00&lt;/td&gt;\n&lt;td&gt;Tests input / output water quality (ppm)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;10&lt;/td&gt;\n&lt;td&gt;Tubing cutter&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;30.00&lt;/td&gt;\n&lt;td&gt;Gives clean, square cuts on RO tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;11&lt;/td&gt;\n&lt;td&gt;Alkaline remineraliser (4″)&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;250.00&lt;/td&gt;\n&lt;td&gt;Adds minerals and raises pH after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;12&lt;/td&gt;\n&lt;td&gt;10″ filter housings&lt;/td&gt;\n&lt;td&gt;3 pcs&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;360.00&lt;/td&gt;\n&lt;td&gt;Hold sediment, CTO and GAC cartridges&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;13&lt;/td&gt;\n&lt;td&gt;¼″ RO tubing (≈ 5 m)&lt;/td&gt;\n&lt;td&gt;2 coils&lt;/td&gt;\n&lt;td&gt;40.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;Connects all filters, pump, membrane and faucet&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;14&lt;/td&gt;\n&lt;td&gt;Two‑side connectors&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Straight unions to join tubing sections&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;15&lt;/td&gt;\n&lt;td&gt;Pump elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;90° elbows for pump inlet/outlet to tubing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;16&lt;/td&gt;\n&lt;td&gt;Membrane elbows&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;5.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Elbows that screw into membrane housing ports&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;17&lt;/td&gt;\n&lt;td&gt;Housing elbows&lt;/td&gt;\n&lt;td&gt;5 pcs&lt;/td&gt;\n&lt;td&gt;9.00&lt;/td&gt;\n&lt;td&gt;45.00&lt;/td&gt;\n&lt;td&gt;Elbow fittings for pre‑filter housings&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;18&lt;/td&gt;\n&lt;td&gt;Inlet‑valve set&lt;/td&gt;\n&lt;td&gt;1 set&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;120.00&lt;/td&gt;\n&lt;td&gt;Shuts off / isolates the water supply to the RO unit&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;19&lt;/td&gt;\n&lt;td&gt;Post‑carbon cartridge&lt;/td&gt;\n&lt;td&gt;1 pc&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;80.00&lt;/td&gt;\n&lt;td&gt;“Polishing” filter that improves taste after RO&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;20&lt;/td&gt;\n&lt;td&gt;C‑clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Hardware for mounting the wall plate / housing&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;21&lt;/td&gt;\n&lt;td&gt;X‑clamp&lt;/td&gt;\n&lt;td&gt;2 pcs&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;20.00&lt;/td&gt;\n&lt;td&gt;Additional mounting support for the RO assembly&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;22&lt;/td&gt;\n&lt;td&gt;PTFE (Teflon) tape&lt;/td&gt;\n&lt;td&gt;10 rolls&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;100.00&lt;/td&gt;\n&lt;td&gt;Seals threaded joints to prevent leaks&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;23&lt;/td&gt;\n&lt;td&gt;General‑purpose tape&lt;/td&gt;\n&lt;td&gt;1 roll&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;10.00&lt;/td&gt;\n&lt;td&gt;Miscellaneous binding / labelling&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Total value of cash‑memo items: ₹4,360&lt;/p&gt;\n&lt;p&gt;Bill of the items bought.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purification-parts-bill-part1.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/water-purifiation-parts-bill-2.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-all-the-parts-are-plumbed-step-by-step&#34;&gt;How all the parts are plumbed – step-by-step&lt;/h2&gt;\n&lt;p&gt;We have 3 big housings, we put the following filters inside them and connect them sequentially. Using similar housing to the spun filter, we fit the other filters and connect them upuntill the motor.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/spun-filter-with-housing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sediment (Spun) filter&lt;/strong&gt;&lt;br /&gt;\nThe very first stage. It traps visible particles such as sand, rust and silt so that none of the downstream cartridges get clogged prematurely.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;CTO 10″ carbon-block filter&lt;/strong&gt;&lt;br /&gt;\nRemoves chlorine, taste and odour. Chlorine must be taken out here because it can irreversibly damage an RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;GAC (pre-carbon) filter&lt;/strong&gt;&lt;br /&gt;\nA second carbon stage that “polishes” the water and adsorbs any remaining organic chemicals that slipped past the CTO block.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booster pump (motor) + low-pressure cut-off&lt;/strong&gt;&lt;br /&gt;\nRight after the pre-carbon filter the water enters the BlueShell booster pump.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;The pump raises the feed-water pressure to ~80 psi so that the RO membrane can work efficiently.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Placing it here ensures that only pre-filtered, de-chlorinated water passes through the pump head, protecting the pump and its check-valves from grit or chlorine attack.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;RO membrane housing with 100 GPD membrane&lt;/strong&gt;&lt;br /&gt;\nUnder high pressure the semi-permeable membrane now removes dissolved salts, heavy metals, fluoride, nitrates, etc.&lt;br /&gt;\n&lt;em&gt;This (following image) is how we fit the RO membrane, we need to make sure the waste water port is not we use.&lt;/em&gt;\n&lt;strong&gt;Another thing to note: The waste (concentrate) port coming out of the housing is fitted with a flow-restrictor to maintain the required pressure differential.&lt;/strong&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/ro-membrane-how-to-attach.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-carbon (polishing) filter&lt;/strong&gt;&lt;br /&gt;\nThe first stage on the product-water side. It adsorbs any residual gases and improves the taste and smell of the permeate coming out of the RO membrane.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alkaline remineraliser&lt;/strong&gt;&lt;br /&gt;\nAdds back healthy minerals (calcium, magnesium, potassium) and gently increases the pH so the water is no longer slightly acidic.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;UV-C chamber (11 W)&lt;/strong&gt;&lt;br /&gt;\nThe final barrier. Ultraviolet light disinfects the remineralised water, inactivating any bacteria, viruses or cysts that might have slipped through or grown in the storage tank.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;From the UV outlet the water goes to the water storage through pipes.&lt;/p&gt;\n&lt;h2 id=&#34;final-setup&#34;&gt;Final setup&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/diy-water-filteration/diy-water-purification-end.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;mounting&#34;&gt;Mounting&lt;/h2&gt;\n&lt;p&gt;Mounted everything with ziptie to a window in kitchen, when everything is connected, leave it running for 20mins, can immediately detect the change, the TDS of purified drinking water bottle that we buy is about 25ppm, I get 19ppm with this setup.&lt;/p&gt;\n&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;\n&lt;p&gt;Central Pollution control Board India\n&lt;a href=&#34;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://cpcb.nic.in/wqm/BIS_Drinking_Water_Specification.pdf&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;persoal-update&#34;&gt;Persoal Update&lt;/h2&gt;\n&lt;p&gt;Feel like everything is stagnenet..even though small progress, If you are working for big corpa, you are just a row in an excel sheet.. the feeling more and more resonates reading trending hackernews and things that happen around in personal experience. As for me, as always trying to make time for open source and my hobbies..in the increasing mist of responsibilites coming towards me. I also almost got into marriage by my dad, forced to go see them.., the girl &amp;amp; girls family likes me and trying to convince me into marriage, my dad doing all this best to set this up, fighting with me..etc, the girl is good too, perfect for family, they are a distant relative, born in 2000.. But there are other plans/ God has others plans. ~future me reading this.. what happened?&lt;/p&gt;</description></item><item><title>JWT Authentication</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&amp;rsquo;rd Frost.) I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. Since I&amp;rsquo;m planning to vacate Chennai in March, after February (Frost&amp;rsquo;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can seamlessly automate the issuance and renewal of wildcard certificates. Gone are the days of logging into my DNS provider’s dashboard every few months. With minimal scripting and a few Cron entries, my wildcard certificates can renew without me lifting a finger.&lt;/p&gt;\n&lt;!-- If this guide was helpful, [check out more of my tech insights at BluePie](https://bluepie.in) or leave a comment below to share your experience or ask questions! --&gt;\n&lt;p&gt;Leave a comment below to share your experience or ask questions!&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;p&gt;See you all soon!&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item><item><title>Updating Dns Servers for Ads and Tracking Protection in 2023</title><link>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</link><pubDate>Sat, 28 Jan 2023 06:02:22 +0000</pubDate><guid>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</guid><description>\n&lt;h2 id=&#34;upgrading-pi-hole&#34;&gt;Upgrading PI hole&lt;/h2&gt;\n&lt;p&gt;Once every year, I do server maintainence (during Dec-Jan), this year I updated the our dns server from &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;pi-hole&lt;/a&gt; to &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AGH&lt;/a&gt; (both are open source projects..) Just wanted to try an alternative, after using it for a while, the UI &amp;amp; Features on AGH is much better than pi-hole.&lt;/p&gt;\n&lt;h2 id=&#34;why-a-custom-dns-server&#34;&gt;Why a custom dns server?&lt;/h2&gt;\n&lt;p&gt;All websites/apis are just ip addresses (just numbers). Nameservers maintains a mapping of the domain names to the ip addresses, so when we make a request, we get the appropriate ip address.&lt;/p&gt;\n&lt;p&gt;The idea is simple, by running our own dns server, we can prevent our network providers spying on us, on top of that we can intercept &amp;amp; block all ads &amp;amp; trackers.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;I get ads, but what are Trackers?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Similar to ads, ever been in a situation where you searched for something in flipkart/amazon.. then later you find the ad to buy the product elsewhere, this is because of trackers.. trackers are worse because they collect your data, the same way we block ads, trackers should be blocked.&lt;/p&gt;\n&lt;h2 id=&#34;how-this-can-help-us-with-ad-trackers-blocking&#34;&gt;How this can help us with ad &amp;amp; trackers blocking?&lt;/h2&gt;\n&lt;p&gt;Can you make a guess how serving ads in app/websites work? they are from a server too, that server has a domain linked to it.. can you see where I am are going with this?&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;good content -&amp;gt; respond with ip address\nad/tracker -&amp;gt; respond with 0.0.0.0\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Not just blocking ads &amp;amp; tracking, you can do much more.. there are other things that you can do in dns level.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;block windows tracking or updates in windows machines&lt;/li&gt;\n&lt;li&gt;blocking spyware&lt;/li&gt;\n&lt;li&gt;disabling websites for a particular client (ex: blocking instagram on a your phone alone)&lt;/li&gt;\n&lt;li&gt;if you don&amp;rsquo;t want to keep tracking of your watch history in youtube\nand many more.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;aren-t-there-other-solutions-to-blocking-trackers-and-ads&#34;&gt;Aren&amp;rsquo;t there other solutions to blocking trackers and ads?&lt;/h3&gt;\n&lt;p&gt;yes but those come with a disadvantage,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Browser extensions.. only applies to browsers (system wide, everywhere and all applications)&lt;/li&gt;\n&lt;li&gt;Android, Iphone (separate apps that use vpn locally to achieve this) - Drains battery alot&lt;/li&gt;\n&lt;li&gt;Rooting android, Jailbreaking ios.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;so this does not require any of that.. no addons, no extra apps, no root, no jailbreak.&lt;/p&gt;\n&lt;h2 id=&#34;dns-over-tls&#34;&gt;DNS Over TLS&lt;/h2&gt;\n&lt;p&gt;Introduced in 2016, main idea is wrapping DNS requests and responses via TLS (Transport Layer Security), ensures observer will not be able to track or tamper the contents of the DNS requests.&lt;/p&gt;\n&lt;!-- ssl - secure socket layer --&gt;\n&lt;!-- tls - tranport layer security, offers end to end security. --&gt;\n&lt;p&gt;Shortly after, android introduced DOT as &amp;ldquo;Private DNS&amp;rdquo; on Android 9 &amp;amp; up, we will be using this feature in our devices to block all ADS &amp;amp; Trackers.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/DOT-noMITM.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;client -&amp;gt; dot server, dot server -&amp;gt; queries the dns.. (we forward our request to serveral dns servers, load-balancers)&lt;/p&gt;\n&lt;p&gt;Traditional DNS vs DOT&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/traditional-vs-dot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-do-we-do-it&#34;&gt;How do we do it?&lt;/h2&gt;\n&lt;p&gt;We use an existing open source dns resolver.&lt;/p&gt;\n&lt;p&gt;One options is to go with &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;PI Hole&lt;/a&gt;, it is a community favourite open source project that has all the bells and whistles for blocking ads and trackers integrated inside it.&lt;/p&gt;\n&lt;p&gt;We will be using &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AG Home&lt;/a&gt;, it is also a popular open source project, which is community maintained. up until lastmonth (dec 2022) I was using PI-hole, switched to AG-Home, just to try something new.&lt;/p&gt;\n&lt;h3 id=&#34;installing&#34;&gt;Installing&lt;/h3&gt;\n&lt;p&gt;I am assuming that you already have a linux box hosted in aws/google/..etc. DOT works on port 853, so you have to allow incoming and outgoing traffic in port 853.&lt;/p&gt;\n&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;\n&lt;p&gt;To make things easier, I will be using docker images to run the software.&lt;/p&gt;\n&lt;p&gt;running a docker container.. command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;\npodman run --name dnsServer\\\n--restart unless-stopped\\\n-p 2346:3000/tcp\\\n-p 1853:853/tcp\\\n-d adguard/adguardhome\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Things to note are, exposed port 3000 is the webport for admin page &amp;amp; 853 is for DOT.&lt;/p&gt;\n&lt;p&gt;We maintain the configs in a yaml file which will be mounted to the container.. here is an example version.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;bind_host: 0.0.0.0\nbind_port: 3000\nbeta_bind_port: 0\nusers:\n# - name: &amp;lt;your-username&amp;gt;\n# password: &amp;lt;your-passowrd&amp;gt;\nauth_attempts: 5\nblock_auth_min: 15\nhttp_proxy: &amp;quot;&amp;quot;\nlanguage: &amp;quot;&amp;quot;\ntheme: auto\ndebug_pprof: false\nweb_session_ttl: 720\ndns:\nbind_hosts:\n- 0.0.0.0\nblocked_response_ttl: 10\nratelimit: 20\nrefuse_any: true\nfastest_timeout: 1s\ntls:\nenabled: true\nserver_name: dns.bluepie.in\nport_https: 443\nport_dns_over_tls: 853\ncertificate_path: /opt/dnsServer/conf/fullchain.pem\nprivate_key_path: /opt/dnsServer/conf/privkey.pem\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once everything is configured correctly, the service will be up and running.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman ps\nb831cf04743d docker.io/adguard/adguardhome:latest --no-check-update... 1 days ago Up 3 weeks ago 0.0.0.0:1853-&amp;gt;853/tcp, 0.0.0.0:2346-&amp;gt;3000/tcp dnsServer\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;configuring-domain&#34;&gt;Configuring Domain&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;First we need a domain name, a paid one if you have it or a free one would be good, I&amp;rsquo;ll be creating a sub-domain from my domain, (i.e) &amp;lsquo;dns.bluepie.in&amp;rsquo; and get SSL certificates chain for the same.&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Inorder to uniquely identify clients, we need to create a wildcard sud-domain in our DNS provider, in my case godaddy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/creating-wild-card-dns.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h4 id=&#34;nginx&#34;&gt;Nginx&lt;/h4&gt;\n&lt;p&gt;We will be using nginx as a reverse proxy for exposing our services to the internet.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Admin Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server_name dns.bluepie.in;\nlocation / {\nproxy_pass http://localhost:2346/;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_redirect / / ;\nproxy_cookie_path / /;\nproxy_connect_timeout 15s;\nproxy_send_timeout 15s;\nproxy_read_timeout 15s;\n}\nlisten 443 ssl;\nssl_certificate /path/to/fullchain.pem;\nssl_certificate_key /path/to/privkey.pem;\ninclude /etc/ssl/options-ssl-nginx.conf;\nssl_dhparam /etc/ssl/ssl-dhparams.pem;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;DNS Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;nginx has a load balancing feature, stream directive to manage tcp and udp. we can use this to forward our requests to dns servers.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;stream {\nlog_format basic &#39;$time_iso8601 $remote_addr &#39;\n&#39;$protocol $status $bytes_sent $bytes_received &#39;\n&#39;$session_time $upstream_addr &#39;\n&#39;&amp;quot;$upstream_bytes_sent&amp;quot; &amp;quot;$upstream_bytes_received&amp;quot; &amp;quot;$upstream_connect_time&amp;quot;&#39;;\naccess_log /var/log/nginx/dns-access.log basic buffer=1k flush=5s;\nupstream dot {\nzone dot 512k;\nserver 127.0.0.1:1853;\n}\nserver {\nlisten 853;\nproxy_pass dot;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;admin-portal&#34;&gt;Admin portal&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;Stats page (configured to refresh every 24 hours)&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-statistics.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Configuring blocklists&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Over the years, I have accustomed to the following blocklists and it works best for me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-blocklists.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Blocklists (entires count taken at the time of writing this post&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# Blocks Ads, Mobile Ads, Phishing, Malvertising, Malware, Tracking, Telemetry, CryptoJacking, Analytics, Spyware, Ransomware, Exploit, Fraud, Abuse, Scam, Spam, Hijack, Misleading Marketing.\n# ~300,000 entries\nhttps://oisd.nl\n# ~180,000 entries\n# Extending and consolidating hosts files from several well-curated sources like adaway.org, mvps.org, malwaredomainlist.com, someonewhocares.org, and potentially others.\ngithub.com/StevenBlack/hosts\n# ~480,000 entries\n# Automatically updated, moderated and optimized list for blocking ads, trackers and other online garbage.\ngithub.com/notracking/hosts-blocklists\n# Specially Designed for Mobile Ad Protection.\n# ~200,000 entries\ngithub.com/jerryn70/GoodbyeAds\n# Blocking mobile ad providers and some analytics providers.\n# ~7,000 entries\ngithub.com/AdAway/adaway.github.io\n# A filter composed of several other filters (AdGuard Base filter, Social media filter, Tracking Protection filter, Mobile Ads filter, EasyList and EasyPrivacy) and simplified specifically to be better compatible with DNS-level ad blocking.\n# ~50,000 entries\ngithub.com/AdguardTeam/AdguardSDNSFilter\n# Protects you from many types of spyware, reduces bandwidth use, blocks certain pop-up traps, prevents user tracking by way of &amp;quot;web bugs&amp;quot; embedded in spam, provides partial protection to IE from certain web-based exploits and blocks most advertising you would otherwise be subjected to on the internet.\n# ~11,000 entries\nsomeonewhocares.org/hosts\n# Filter that blocks ads on mobile devices. Contains all known mobile ad networks.\n# ~1,000 entries\nkb.adguard.com/general/adguard-ad-filters#mobile-ads-filter\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-do-we-use-it&#34;&gt;How do we use it?&lt;/h2&gt;\n&lt;p&gt;Pick a unique word, ex: gopidevice1, then use this word as prefix for &amp;lsquo;dns.bluepie.in&amp;rsquo;. (i.e) &amp;lsquo;gopidevice1.dns.bluepie.in&amp;rsquo;, this is the url that you should use in your devices.&lt;/p&gt;\n&lt;h3 id=&#34;android&#34;&gt;Android&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/android-config.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;ios-iphone-ipad&#34;&gt;IOS - iphone, ipad&lt;/h3&gt;\n&lt;p&gt;Go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;, navigate to &amp;lsquo;tool&amp;rsquo;, give a name and use your unique url to create a profile.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;mac-windows&#34;&gt;Mac/Windows&lt;/h3&gt;\n&lt;p&gt;Same as iphone, go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dot-install-macos-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Migrating from PI-Hole to AGH was a good move, once you have used this and experienced &amp;ldquo;no ads&amp;rdquo;, you can not go back~&lt;/p&gt;</description></item><item><title>Modding a macOS App</title><link>//bluepie.in/2022/02/modding-a-macos-app/</link><pubDate>Sun, 20 Feb 2022 19:10:13 +0000</pubDate><guid>//bluepie.in/2022/02/modding-a-macos-app/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;This post is about reversing.. but for a MACOS binary. Lets drive straight into it.&lt;/p&gt;\n&lt;p&gt;Modding in macOS is fairly new to me, but a binary is a binary and it can be modded!. I started to daily drive macOS a month or two ago, the why and what&amp;rsquo;s is a whole different story to post about.&lt;/p&gt;\n&lt;p&gt;Like I have said previously in my other &lt;a href=&#34;https://bluepie.in/categories/reversing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;re posts&lt;/a&gt;, &amp;ldquo; I don&amp;rsquo;t trust mods from the internet&amp;rdquo; If I can mod it then its fine, otherwise I&amp;rsquo;m paying for it.&lt;/p&gt;\n&lt;p&gt;I just started to understand the quirks of &amp;ldquo;OS X&amp;rdquo;, Though some of my methods may be crude&amp;hellip;it works, I will perfect them..&lt;/p&gt;\n&lt;h2 id=&#34;target&#34;&gt;Target&lt;/h2&gt;\n&lt;p&gt;So there is this great app for cleaning your Mac&amp;rsquo;s junk/storage files, but the free version only lets you clean 500mb daily.. the premium version is a whooping 3k per year&amp;hellip;do you get where I am going with this ? &amp;hellip;YES!&amp;hellip;modding for fun and profit (PROFIT = personal use)&lt;/p&gt;\n&lt;h2 id=&#34;tools&#34;&gt;Tools&lt;/h2&gt;\n&lt;p&gt;So I&amp;rsquo;m gonna be trying out ghidra &amp;ldquo; The NSA&amp;rsquo;s reverse engineering tool &amp;ldquo;, mainly because Its a open source tool and I wanted to do some reverse engineering with it for a long time now.&lt;/p&gt;\n&lt;p&gt;make sure you have &lt;a href=&#34;https://brew.sh/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;brew&lt;/a&gt; installed and then run the following command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-console&#34;&gt;brew install --cask ghidra\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;making-observation-to-the-target&#34;&gt;Making observation to the target&lt;/h2&gt;\n&lt;p&gt;The app throws you with the &amp;ldquo;Nag Screen&amp;rdquo; whenever you use the app, it blocks the usage of features (can not use the app to remove more than 500MB of data,privacy scanning won&amp;rsquo;t work unless you pay,..etc).&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;500mb storage limit reached &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/500mb-over-nag-screen.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Trial period expired &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/clean-my-mac-unmodded.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;basics-understanding-ghidra&#34;&gt;Basics &amp;amp; Understanding Ghidra&lt;/h2&gt;\n&lt;p&gt;All macOS apps have the following structure, the main binary will always be inside the MacOS directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/mac-app-folder-structure.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Launch Ghidra and create a new project, then drag and drop the binary into the project.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-new-project.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Mac has this binary format &amp;ldquo;macobinary&amp;rdquo; - basically it contains the different target executables, ex: (arm binary vs the x86 binary).. mine is a x86 machine, will be modding that alone.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-appropriate-target-binary.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;After the import, Ghidra will automatically ask you to analyse the target binary, choose Analyse. (it will make our life easier), based on the binary size and your cpu cores, it may take couple minutes.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/will-ask-for-analyse-choose-analyse.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Only focus on the following five windows, that is enough to get the job done.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/ghidra-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;1 = will contain the symbol tabel, this is where we do most of the filtering.&lt;/li&gt;\n&lt;li&gt;2 = shows the disassembly of the binary (assembly language), this is where we do the actual reversing, (i.e) patching, understanding logic, etc.&lt;/li&gt;\n&lt;li&gt;3 = This is a great feature, shows the equivalent c code, to the selected assembly section.&lt;/li&gt;\n&lt;li&gt;4 &amp;amp; 5 = shows the incoming and outgoing function calls, this will help us find interesting things about the binary, which then can be used for identifying logic and patching.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;IF ANY OF THE ABOVE WINDOWS ARE NOT OPEN, YOU CAN ENABLE THEM THROUGH THE MENU OPTION &amp;ldquo;WINDOW&amp;rdquo;.&lt;/p&gt;\n&lt;h2 id=&#34;analysis&#34;&gt;Analysis&lt;/h2&gt;\n&lt;p&gt;It will take some time to find what you are looking for, but I have already invested time in this, so what we need is &amp;ldquo;activationManager&amp;rdquo; class, it has all sorts of interesting functions like &amp;ldquo;activationStatus, isAppActivated..etc&amp;rdquo;. Looking for references, it is used throughout the app, so lets change it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/different-app-status.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Looking around, we find the different status codes inside &amp;ldquo;activationStatus&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;By checking other functions, it becomes obvious that, changing only &amp;ldquo;isAppActivated&amp;rdquo; function is enough. The return type of the function is &amp;ldquo;char&amp;rdquo;, with reference to &amp;ldquo;status&amp;rdquo; from above..we should return &amp;ldquo;3&amp;rdquo;. From the ascii table, &amp;ldquo;0x33&amp;rdquo; is the equivalnet for &amp;ldquo;3&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;Patching the &amp;ldquo;isAppActivated&amp;rdquo; function, with the following code.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;push rbp\nmov rbp,rsp\nmov DWORD PTR [rbp-0x4],0x0\nmov eax,0x33\npop rbp\nret\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then export the program &amp;ldquo;File -&amp;gt; Export Program&amp;rdquo;, our patched binary is ready. Now give executable permission and move it to the original directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/move-exported-binary-to-original-directory.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;rename the original&lt;/p&gt;\n&lt;h2 id=&#34;signing-running-the-modded-binary-for-profit&#34;&gt;Signing &amp;amp; Running the modded binary for PROFIT&lt;/h2&gt;\n&lt;p&gt;Unlike windows and linux, macOS won&amp;rsquo;t run applications without signing the binary.. but we can generate your own certificates for personal use (dont have to buy a developer account for 99$/year).&lt;/p&gt;\n&lt;p&gt;create a certificate using &amp;ldquo;keychain Access&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-custom-certifcate.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;choose &amp;ldquo;code signing&amp;rdquo; in &amp;ldquo;certificate type&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-code-signing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Go to the application root &amp;ldquo;/Application/&amp;rdquo; to resign the entire app.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo codesign -s &amp;quot;&amp;lt;name-of-certificate&amp;gt;&amp;quot; -fv CleanMyMac-MAS.app/\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Run the app&amp;hellip; Now its been modded and I don&amp;rsquo;t have to pay 3k INR a year or worry about untrusted binaries from the internet.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/removed-files-beyong-the-limit.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;You can see from the image, all the premium features are now unlocked &amp;amp; we can also remove the junk files exceeding 500MB.&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;This was a fun excerise to dig around. Macos is not that limited as I thought it was&amp;hellip; Sure it has its quirks.&lt;/p&gt;</description></item></channel></rss>"}, "ae47d92304c9cb03b63c33da9148f63275a9abe1": {"t": 1785770793.7514277, "n": 158920, "h": "c647290755f7cda9", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sun, 02 Feb 2025 16:10:12 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>JWT Authentication</title><link>//bluepie.in/2025/02/jwt-auth/</link><pubDate>Sun, 02 Feb 2025 16:10:12 +0000</pubDate><guid>//bluepie.in/2025/02/jwt-auth/</guid><description>\n&lt;h3 id=&#34;introduction&#34;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; I’m documenting this so that I can refer back to it in the future. Recently, I’ve been working with Spring WebFlux, and although I’ve previously used JWTs, this is my first time writing about them. I often forget the implementation details when switching between languages or frameworks, so I’m recording this while it’s still fresh in my mind.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;JSON Web Tokens (&lt;strong&gt;JWTs&lt;/strong&gt;) are a compact way to prove a user’s identity between two parties—usually a browser and your API.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;token-life-cycle&#34;&gt;&lt;strong&gt;Token life-cycle&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;A JWT is &lt;em&gt;self-contained&lt;/em&gt;: it carries the user’s ID, roles, and an &lt;strong&gt;expiry&lt;/strong&gt; inside the token itself.&lt;br /&gt;\nTo balance security and convenience I use &lt;strong&gt;two clocks&lt;/strong&gt;:&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Clock&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What it limits&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Typical value&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;What happens when it hits zero&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Access expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the current token stays valid&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;15–30 min&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;Client must call &lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Refresh expiry&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;How long the &lt;em&gt;refresh token&lt;/em&gt; can mint new access tokens&lt;/td&gt;\n&lt;td&gt;&lt;em&gt;7–30 days&lt;/em&gt;&lt;/td&gt;\n&lt;td&gt;User must log in again&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;Flow:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;/login ──► access + refresh token\n│\n(before access expiry)\n▼\n/refresh ──► new access token\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After the &lt;strong&gt;refresh expiry&lt;/strong&gt; passes, even the refresh endpoint refuses to issue new tokens—forcing a fresh login.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;building-blocks&#34;&gt;&lt;strong&gt;Building blocks&lt;/strong&gt;&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; – database tables for users &amp;amp; login history&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; – Reactive CRUD interfaces provided by Spring Data R2DBC&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; – business logic (password checks, audit log)&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Auth manager&lt;/strong&gt; – validates credentials or JWT claims&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; – extracts the Authorization: Bearer … header&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security config&lt;/strong&gt; – wires it all together&lt;/li&gt;\n&lt;/ol&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;essential-code&#34;&gt;&lt;strong&gt;Essential code&lt;/strong&gt;&lt;/h2&gt;\n&lt;h4 id=&#34;entity-classes&#34;&gt;&lt;strong&gt;Entity Classes&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These classes represent database tables as Java objects using Spring Data R2DBC. &lt;code&gt;@Table&lt;/code&gt; maps the class to a table, and Lombok annotations (&lt;code&gt;@Data&lt;/code&gt;, &lt;code&gt;@Builder&lt;/code&gt;, etc.) generate getters, setters, constructors, and a builder.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Table(&amp;quot;users&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class UserEntity {\n@Id private Long id;\nprivate String username;\nprivate String password; // hashed\nprivate String roles; // e.g. &amp;quot;USER,ADMIN&amp;quot;\nprivate boolean enabled;\n}\n@Table(&amp;quot;login_history&amp;quot;)\n@Data @Builder @NoArgsConstructor @AllArgsConstructor\npublic class LoginHistory {\n@Id private Long id;\nprivate Long userId;\nprivate LocalDateTime loginTime;\nprivate String ipAddress;\nprivate boolean successful;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;repositories&#34;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Spring Data’s &lt;code&gt;ReactiveCrudRepository&lt;/code&gt; provides reactive CRUD operations. By extending these interfaces, Spring auto-generates the implementation at runtime.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public interface UserRepository\nextends ReactiveCrudRepository&amp;lt;UserEntity, Long&amp;gt; {\nMono&amp;lt;UserEntity&amp;gt; findByUsername(String username);\n}\npublic interface LoginHistoryRepository\nextends ReactiveCrudRepository&amp;lt;LoginHistory, Long&amp;gt; {}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;jwt-filter-excerpt&#34;&gt;&lt;strong&gt;JWT filter&lt;/strong&gt; (excerpt)&lt;/h4&gt;\n&lt;p&gt;&lt;code&gt;ServerAuthenticationConverter&lt;/code&gt; is a Spring Security WebFlux interface for converting HTTP requests into &lt;code&gt;Authentication&lt;/code&gt; objects. In this converter, we extract and validate the JWT token from the Authorization header.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Slf4j\npublic class JwtAuthConverter implements ServerAuthenticationConverter {\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; convert(ServerWebExchange exchange) {\nString header = exchange.getRequest()\n.getHeaders()\n.getFirst(HttpHeaders.AUTHORIZATION);\nif (header == null || !header.startsWith(&amp;quot;Bearer &amp;quot;)) return Mono.empty();\ntry {\nString token = header.substring(7);\nJws&amp;lt;Claims&amp;gt; claims = JwtUtil.validateTokenAndGetClaims(token);\nString username = claims.getBody().getSubject();\nList&amp;lt;String&amp;gt; roles = claims.getBody().get(&amp;quot;roles&amp;quot;, List.class);\nList&amp;lt;GrantedAuthority&amp;gt; authorities = roles.stream()\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn Mono.just(new UsernamePasswordAuthenticationToken(\nusername, null, authorities));\n} catch (Exception ex) {\nlog.warn(&amp;quot;Invalid token: {}&amp;quot;, ex.getMessage());\nreturn Mono.empty();\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;security-configuration-simplified&#34;&gt;&lt;strong&gt;Security configuration&lt;/strong&gt; (simplified)&lt;/h4&gt;\n&lt;p&gt;The &lt;code&gt;@Configuration&lt;/code&gt; class defines beans for Spring’s application context. &lt;code&gt;@EnableWebFluxSecurity&lt;/code&gt; activates WebFlux security support. We configure a &lt;code&gt;SecurityWebFilterChain&lt;/code&gt; bean to set up CSRF, CORS, endpoint permissions, and insert our JWT filter.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n@Bean\npublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\nJwtAuthWebFilter jwt = new JwtAuthWebFilter(new JwtReactiveAuthManager());\njwt.setServerAuthenticationConverter(new JwtAuthConverter());\nreturn http\n.csrf(ServerHttpSecurity.CsrfSpec::disable)\n.cors(ServerHttpSecurity.CorsSpec::disable)\n.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n.formLogin(ServerHttpSecurity.FormLoginSpec::disable)\n.exceptionHandling(e -&amp;gt; e.authenticationEntryPoint(\n(exch, ex) -&amp;gt; {\nexch.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\nreturn exch.getResponse().setComplete();\n}))\n.authorizeExchange(ex -&amp;gt; ex\n.pathMatchers(\n&amp;quot;/api/v1/auth/login&amp;quot;,\n&amp;quot;/api/v1/auth/refresh&amp;quot;,\n&amp;quot;/api/v1/auth/validate&amp;quot;,\n&amp;quot;/actuator/**&amp;quot;)\n.permitAll()\n.pathMatchers(&amp;quot;/api/v1/task/**&amp;quot;).authenticated())\n.addFilterAt(jwt, SecurityWebFiltersOrder.AUTHENTICATION)\n.build();\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h4 id=&#34;reactive-authentication-manager&#34;&gt;&lt;strong&gt;Reactive authentication manager&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;Implementing &lt;code&gt;ReactiveAuthenticationManager&lt;/code&gt; allows custom authentication logic in WebFlux. This manager checks credentials, handles disabled accounts, and records login attempts via the &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;@Component\n@RequiredArgsConstructor\npublic class DatabaseReactiveAuthManager implements ReactiveAuthenticationManager {\nprivate final UserService users;\n@Override\npublic Mono&amp;lt;Authentication&amp;gt; authenticate(Authentication auth) {\nString username = auth.getName();\nString rawPwd = auth.getCredentials().toString();\nreturn users.findByUsername(username)\n.switchIfEmpty(Mono.error(new BadCredentialsException(&amp;quot;User not found&amp;quot;)))\n.flatMap(u -&amp;gt; {\nif (!users.checkPassword(rawPwd, u.getPassword()))\nreturn users.recordLoginAttempt(u, false, null)\n.then(Mono.error(new BadCredentialsException(&amp;quot;Bad password&amp;quot;)));\nif (!u.isEnabled())\nreturn Mono.error(new DisabledException(&amp;quot;Account disabled&amp;quot;));\nList&amp;lt;GrantedAuthority&amp;gt; roles = Arrays.stream(u.getRoles().split(&amp;quot;,&amp;quot;))\n.map(String::trim)\n.map(SimpleGrantedAuthority::new)\n.collect(Collectors.toList());\nreturn users.recordLoginAttempt(u, true, null)\n.thenReturn(new UsernamePasswordAuthenticationToken(\nusername, null, roles));\n});\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h4 id=&#34;configuration-properties&#34;&gt;&lt;strong&gt;Configuration properties&lt;/strong&gt;&lt;/h4&gt;\n&lt;p&gt;These YAML properties configure Spring Security’s JWT support, specifying the signing &lt;strong&gt;secret&lt;/strong&gt; and token lifetimes.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;security:\nencryption:\nsecret: _&amp;quot;encryption_secret&amp;quot;_\njwt:\nexpiration-time: __1_440_000__ # 24 minutes (ms)\nrefresh-expiration-time: __604_800_000__ # 7 days (ms)\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Tip – generate a strong secret:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-py&#34;&gt;# This one-off command uses Docker to generate a bcrypt hash for a random 32-byte secret\ndocker run --rm python:3.9-slim \\\npython - &amp;lt;&amp;lt;&#39;PY&#39;\nimport bcrypt, os\nprint(bcrypt.hashpw(os.urandom(32), bcrypt.gensalt()).decode())\nPY\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;recap&#34;&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Two expiries&lt;/strong&gt; keep sessions short yet user-friendly.&lt;/li&gt;\n&lt;li&gt;A reactive filter &lt;strong&gt;parses&lt;/strong&gt; the token and attaches &lt;strong&gt;authorities&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;A database auth manager handles &lt;strong&gt;login&lt;/strong&gt;, &lt;strong&gt;password checks&lt;/strong&gt;, and &lt;strong&gt;audit logs&lt;/strong&gt;.&lt;/li&gt;\n&lt;li&gt;Spring WebFlux security is glued together with one concise &lt;strong&gt;&lt;code&gt;SecurityWebFilterChain&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;references&#34;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://jwt.io/introduction/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JWT.io Introduction&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/jwtk/jjwt#readme&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;JJWT (Java JWT) Documentation&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;&lt;strong&gt;Personal Update&lt;/strong&gt;&lt;/h2&gt;\n&lt;p&gt;Setup JioFiber at home, disconnected Airtel in Chennai, and ported all my SIMs to Jio except mom’s. Jio offers unlimited data (4G/5G) on individual plans for $8.77 per month. Nowadays I don’t have enough time to do anything. No no no…not gonna start a rant here…lol. Talked to a friend about old WiFi hacking days…(more in the next few posts).&lt;/p&gt;</description></item><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&amp;rsquo;rd Frost.) I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. Since I&amp;rsquo;m planning to vacate Chennai in March, after February (Frost&amp;rsquo;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can seamlessly automate the issuance and renewal of wildcard certificates. Gone are the days of logging into my DNS provider’s dashboard every few months. With minimal scripting and a few Cron entries, my wildcard certificates can renew without me lifting a finger.&lt;/p&gt;\n&lt;!-- If this guide was helpful, [check out more of my tech insights at BluePie](https://bluepie.in) or leave a comment below to share your experience or ask questions! --&gt;\n&lt;p&gt;Leave a comment below to share your experience or ask questions!&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;p&gt;See you all soon!&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item><item><title>Updating Dns Servers for Ads and Tracking Protection in 2023</title><link>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</link><pubDate>Sat, 28 Jan 2023 06:02:22 +0000</pubDate><guid>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</guid><description>\n&lt;h2 id=&#34;upgrading-pi-hole&#34;&gt;Upgrading PI hole&lt;/h2&gt;\n&lt;p&gt;Once every year, I do server maintainence (during Dec-Jan), this year I updated the our dns server from &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;pi-hole&lt;/a&gt; to &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AGH&lt;/a&gt; (both are open source projects..) Just wanted to try an alternative, after using it for a while, the UI &amp;amp; Features on AGH is much better than pi-hole.&lt;/p&gt;\n&lt;h2 id=&#34;why-a-custom-dns-server&#34;&gt;Why a custom dns server?&lt;/h2&gt;\n&lt;p&gt;All websites/apis are just ip addresses (just numbers). Nameservers maintains a mapping of the domain names to the ip addresses, so when we make a request, we get the appropriate ip address.&lt;/p&gt;\n&lt;p&gt;The idea is simple, by running our own dns server, we can prevent our network providers spying on us, on top of that we can intercept &amp;amp; block all ads &amp;amp; trackers.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;I get ads, but what are Trackers?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Similar to ads, ever been in a situation where you searched for something in flipkart/amazon.. then later you find the ad to buy the product elsewhere, this is because of trackers.. trackers are worse because they collect your data, the same way we block ads, trackers should be blocked.&lt;/p&gt;\n&lt;h2 id=&#34;how-this-can-help-us-with-ad-trackers-blocking&#34;&gt;How this can help us with ad &amp;amp; trackers blocking?&lt;/h2&gt;\n&lt;p&gt;Can you make a guess how serving ads in app/websites work? they are from a server too, that server has a domain linked to it.. can you see where I am are going with this?&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;good content -&amp;gt; respond with ip address\nad/tracker -&amp;gt; respond with 0.0.0.0\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Not just blocking ads &amp;amp; tracking, you can do much more.. there are other things that you can do in dns level.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;block windows tracking or updates in windows machines&lt;/li&gt;\n&lt;li&gt;blocking spyware&lt;/li&gt;\n&lt;li&gt;disabling websites for a particular client (ex: blocking instagram on a your phone alone)&lt;/li&gt;\n&lt;li&gt;if you don&amp;rsquo;t want to keep tracking of your watch history in youtube\nand many more.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;aren-t-there-other-solutions-to-blocking-trackers-and-ads&#34;&gt;Aren&amp;rsquo;t there other solutions to blocking trackers and ads?&lt;/h3&gt;\n&lt;p&gt;yes but those come with a disadvantage,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Browser extensions.. only applies to browsers (system wide, everywhere and all applications)&lt;/li&gt;\n&lt;li&gt;Android, Iphone (separate apps that use vpn locally to achieve this) - Drains battery alot&lt;/li&gt;\n&lt;li&gt;Rooting android, Jailbreaking ios.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;so this does not require any of that.. no addons, no extra apps, no root, no jailbreak.&lt;/p&gt;\n&lt;h2 id=&#34;dns-over-tls&#34;&gt;DNS Over TLS&lt;/h2&gt;\n&lt;p&gt;Introduced in 2016, main idea is wrapping DNS requests and responses via TLS (Transport Layer Security), ensures observer will not be able to track or tamper the contents of the DNS requests.&lt;/p&gt;\n&lt;!-- ssl - secure socket layer --&gt;\n&lt;!-- tls - tranport layer security, offers end to end security. --&gt;\n&lt;p&gt;Shortly after, android introduced DOT as &amp;ldquo;Private DNS&amp;rdquo; on Android 9 &amp;amp; up, we will be using this feature in our devices to block all ADS &amp;amp; Trackers.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/DOT-noMITM.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;client -&amp;gt; dot server, dot server -&amp;gt; queries the dns.. (we forward our request to serveral dns servers, load-balancers)&lt;/p&gt;\n&lt;p&gt;Traditional DNS vs DOT&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/traditional-vs-dot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-do-we-do-it&#34;&gt;How do we do it?&lt;/h2&gt;\n&lt;p&gt;We use an existing open source dns resolver.&lt;/p&gt;\n&lt;p&gt;One options is to go with &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;PI Hole&lt;/a&gt;, it is a community favourite open source project that has all the bells and whistles for blocking ads and trackers integrated inside it.&lt;/p&gt;\n&lt;p&gt;We will be using &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AG Home&lt;/a&gt;, it is also a popular open source project, which is community maintained. up until lastmonth (dec 2022) I was using PI-hole, switched to AG-Home, just to try something new.&lt;/p&gt;\n&lt;h3 id=&#34;installing&#34;&gt;Installing&lt;/h3&gt;\n&lt;p&gt;I am assuming that you already have a linux box hosted in aws/google/..etc. DOT works on port 853, so you have to allow incoming and outgoing traffic in port 853.&lt;/p&gt;\n&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;\n&lt;p&gt;To make things easier, I will be using docker images to run the software.&lt;/p&gt;\n&lt;p&gt;running a docker container.. command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;\npodman run --name dnsServer\\\n--restart unless-stopped\\\n-p 2346:3000/tcp\\\n-p 1853:853/tcp\\\n-d adguard/adguardhome\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Things to note are, exposed port 3000 is the webport for admin page &amp;amp; 853 is for DOT.&lt;/p&gt;\n&lt;p&gt;We maintain the configs in a yaml file which will be mounted to the container.. here is an example version.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;bind_host: 0.0.0.0\nbind_port: 3000\nbeta_bind_port: 0\nusers:\n# - name: &amp;lt;your-username&amp;gt;\n# password: &amp;lt;your-passowrd&amp;gt;\nauth_attempts: 5\nblock_auth_min: 15\nhttp_proxy: &amp;quot;&amp;quot;\nlanguage: &amp;quot;&amp;quot;\ntheme: auto\ndebug_pprof: false\nweb_session_ttl: 720\ndns:\nbind_hosts:\n- 0.0.0.0\nblocked_response_ttl: 10\nratelimit: 20\nrefuse_any: true\nfastest_timeout: 1s\ntls:\nenabled: true\nserver_name: dns.bluepie.in\nport_https: 443\nport_dns_over_tls: 853\ncertificate_path: /opt/dnsServer/conf/fullchain.pem\nprivate_key_path: /opt/dnsServer/conf/privkey.pem\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once everything is configured correctly, the service will be up and running.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman ps\nb831cf04743d docker.io/adguard/adguardhome:latest --no-check-update... 1 days ago Up 3 weeks ago 0.0.0.0:1853-&amp;gt;853/tcp, 0.0.0.0:2346-&amp;gt;3000/tcp dnsServer\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;configuring-domain&#34;&gt;Configuring Domain&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;First we need a domain name, a paid one if you have it or a free one would be good, I&amp;rsquo;ll be creating a sub-domain from my domain, (i.e) &amp;lsquo;dns.bluepie.in&amp;rsquo; and get SSL certificates chain for the same.&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Inorder to uniquely identify clients, we need to create a wildcard sud-domain in our DNS provider, in my case godaddy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/creating-wild-card-dns.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h4 id=&#34;nginx&#34;&gt;Nginx&lt;/h4&gt;\n&lt;p&gt;We will be using nginx as a reverse proxy for exposing our services to the internet.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Admin Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server_name dns.bluepie.in;\nlocation / {\nproxy_pass http://localhost:2346/;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_redirect / / ;\nproxy_cookie_path / /;\nproxy_connect_timeout 15s;\nproxy_send_timeout 15s;\nproxy_read_timeout 15s;\n}\nlisten 443 ssl;\nssl_certificate /path/to/fullchain.pem;\nssl_certificate_key /path/to/privkey.pem;\ninclude /etc/ssl/options-ssl-nginx.conf;\nssl_dhparam /etc/ssl/ssl-dhparams.pem;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;DNS Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;nginx has a load balancing feature, stream directive to manage tcp and udp. we can use this to forward our requests to dns servers.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;stream {\nlog_format basic &#39;$time_iso8601 $remote_addr &#39;\n&#39;$protocol $status $bytes_sent $bytes_received &#39;\n&#39;$session_time $upstream_addr &#39;\n&#39;&amp;quot;$upstream_bytes_sent&amp;quot; &amp;quot;$upstream_bytes_received&amp;quot; &amp;quot;$upstream_connect_time&amp;quot;&#39;;\naccess_log /var/log/nginx/dns-access.log basic buffer=1k flush=5s;\nupstream dot {\nzone dot 512k;\nserver 127.0.0.1:1853;\n}\nserver {\nlisten 853;\nproxy_pass dot;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;admin-portal&#34;&gt;Admin portal&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;Stats page (configured to refresh every 24 hours)&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-statistics.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Configuring blocklists&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Over the years, I have accustomed to the following blocklists and it works best for me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-blocklists.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Blocklists (entires count taken at the time of writing this post&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# Blocks Ads, Mobile Ads, Phishing, Malvertising, Malware, Tracking, Telemetry, CryptoJacking, Analytics, Spyware, Ransomware, Exploit, Fraud, Abuse, Scam, Spam, Hijack, Misleading Marketing.\n# ~300,000 entries\nhttps://oisd.nl\n# ~180,000 entries\n# Extending and consolidating hosts files from several well-curated sources like adaway.org, mvps.org, malwaredomainlist.com, someonewhocares.org, and potentially others.\ngithub.com/StevenBlack/hosts\n# ~480,000 entries\n# Automatically updated, moderated and optimized list for blocking ads, trackers and other online garbage.\ngithub.com/notracking/hosts-blocklists\n# Specially Designed for Mobile Ad Protection.\n# ~200,000 entries\ngithub.com/jerryn70/GoodbyeAds\n# Blocking mobile ad providers and some analytics providers.\n# ~7,000 entries\ngithub.com/AdAway/adaway.github.io\n# A filter composed of several other filters (AdGuard Base filter, Social media filter, Tracking Protection filter, Mobile Ads filter, EasyList and EasyPrivacy) and simplified specifically to be better compatible with DNS-level ad blocking.\n# ~50,000 entries\ngithub.com/AdguardTeam/AdguardSDNSFilter\n# Protects you from many types of spyware, reduces bandwidth use, blocks certain pop-up traps, prevents user tracking by way of &amp;quot;web bugs&amp;quot; embedded in spam, provides partial protection to IE from certain web-based exploits and blocks most advertising you would otherwise be subjected to on the internet.\n# ~11,000 entries\nsomeonewhocares.org/hosts\n# Filter that blocks ads on mobile devices. Contains all known mobile ad networks.\n# ~1,000 entries\nkb.adguard.com/general/adguard-ad-filters#mobile-ads-filter\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-do-we-use-it&#34;&gt;How do we use it?&lt;/h2&gt;\n&lt;p&gt;Pick a unique word, ex: gopidevice1, then use this word as prefix for &amp;lsquo;dns.bluepie.in&amp;rsquo;. (i.e) &amp;lsquo;gopidevice1.dns.bluepie.in&amp;rsquo;, this is the url that you should use in your devices.&lt;/p&gt;\n&lt;h3 id=&#34;android&#34;&gt;Android&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/android-config.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;ios-iphone-ipad&#34;&gt;IOS - iphone, ipad&lt;/h3&gt;\n&lt;p&gt;Go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;, navigate to &amp;lsquo;tool&amp;rsquo;, give a name and use your unique url to create a profile.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;mac-windows&#34;&gt;Mac/Windows&lt;/h3&gt;\n&lt;p&gt;Same as iphone, go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dot-install-macos-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Migrating from PI-Hole to AGH was a good move, once you have used this and experienced &amp;ldquo;no ads&amp;rdquo;, you can not go back~&lt;/p&gt;</description></item><item><title>Modding a macOS App</title><link>//bluepie.in/2022/02/modding-a-macos-app/</link><pubDate>Sun, 20 Feb 2022 19:10:13 +0000</pubDate><guid>//bluepie.in/2022/02/modding-a-macos-app/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;This post is about reversing.. but for a MACOS binary. Lets drive straight into it.&lt;/p&gt;\n&lt;p&gt;Modding in macOS is fairly new to me, but a binary is a binary and it can be modded!. I started to daily drive macOS a month or two ago, the why and what&amp;rsquo;s is a whole different story to post about.&lt;/p&gt;\n&lt;p&gt;Like I have said previously in my other &lt;a href=&#34;https://bluepie.in/categories/reversing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;re posts&lt;/a&gt;, &amp;ldquo; I don&amp;rsquo;t trust mods from the internet&amp;rdquo; If I can mod it then its fine, otherwise I&amp;rsquo;m paying for it.&lt;/p&gt;\n&lt;p&gt;I just started to understand the quirks of &amp;ldquo;OS X&amp;rdquo;, Though some of my methods may be crude&amp;hellip;it works, I will perfect them..&lt;/p&gt;\n&lt;h2 id=&#34;target&#34;&gt;Target&lt;/h2&gt;\n&lt;p&gt;So there is this great app for cleaning your Mac&amp;rsquo;s junk/storage files, but the free version only lets you clean 500mb daily.. the premium version is a whooping 3k per year&amp;hellip;do you get where I am going with this ? &amp;hellip;YES!&amp;hellip;modding for fun and profit (PROFIT = personal use)&lt;/p&gt;\n&lt;h2 id=&#34;tools&#34;&gt;Tools&lt;/h2&gt;\n&lt;p&gt;So I&amp;rsquo;m gonna be trying out ghidra &amp;ldquo; The NSA&amp;rsquo;s reverse engineering tool &amp;ldquo;, mainly because Its a open source tool and I wanted to do some reverse engineering with it for a long time now.&lt;/p&gt;\n&lt;p&gt;make sure you have &lt;a href=&#34;https://brew.sh/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;brew&lt;/a&gt; installed and then run the following command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-console&#34;&gt;brew install --cask ghidra\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;making-observation-to-the-target&#34;&gt;Making observation to the target&lt;/h2&gt;\n&lt;p&gt;The app throws you with the &amp;ldquo;Nag Screen&amp;rdquo; whenever you use the app, it blocks the usage of features (can not use the app to remove more than 500MB of data,privacy scanning won&amp;rsquo;t work unless you pay,..etc).&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;500mb storage limit reached &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/500mb-over-nag-screen.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Trial period expired &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/clean-my-mac-unmodded.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;basics-understanding-ghidra&#34;&gt;Basics &amp;amp; Understanding Ghidra&lt;/h2&gt;\n&lt;p&gt;All macOS apps have the following structure, the main binary will always be inside the MacOS directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/mac-app-folder-structure.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Launch Ghidra and create a new project, then drag and drop the binary into the project.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-new-project.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Mac has this binary format &amp;ldquo;macobinary&amp;rdquo; - basically it contains the different target executables, ex: (arm binary vs the x86 binary).. mine is a x86 machine, will be modding that alone.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-appropriate-target-binary.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;After the import, Ghidra will automatically ask you to analyse the target binary, choose Analyse. (it will make our life easier), based on the binary size and your cpu cores, it may take couple minutes.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/will-ask-for-analyse-choose-analyse.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Only focus on the following five windows, that is enough to get the job done.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/ghidra-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;1 = will contain the symbol tabel, this is where we do most of the filtering.&lt;/li&gt;\n&lt;li&gt;2 = shows the disassembly of the binary (assembly language), this is where we do the actual reversing, (i.e) patching, understanding logic, etc.&lt;/li&gt;\n&lt;li&gt;3 = This is a great feature, shows the equivalent c code, to the selected assembly section.&lt;/li&gt;\n&lt;li&gt;4 &amp;amp; 5 = shows the incoming and outgoing function calls, this will help us find interesting things about the binary, which then can be used for identifying logic and patching.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;IF ANY OF THE ABOVE WINDOWS ARE NOT OPEN, YOU CAN ENABLE THEM THROUGH THE MENU OPTION &amp;ldquo;WINDOW&amp;rdquo;.&lt;/p&gt;\n&lt;h2 id=&#34;analysis&#34;&gt;Analysis&lt;/h2&gt;\n&lt;p&gt;It will take some time to find what you are looking for, but I have already invested time in this, so what we need is &amp;ldquo;activationManager&amp;rdquo; class, it has all sorts of interesting functions like &amp;ldquo;activationStatus, isAppActivated..etc&amp;rdquo;. Looking for references, it is used throughout the app, so lets change it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/different-app-status.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Looking around, we find the different status codes inside &amp;ldquo;activationStatus&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;By checking other functions, it becomes obvious that, changing only &amp;ldquo;isAppActivated&amp;rdquo; function is enough. The return type of the function is &amp;ldquo;char&amp;rdquo;, with reference to &amp;ldquo;status&amp;rdquo; from above..we should return &amp;ldquo;3&amp;rdquo;. From the ascii table, &amp;ldquo;0x33&amp;rdquo; is the equivalnet for &amp;ldquo;3&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;Patching the &amp;ldquo;isAppActivated&amp;rdquo; function, with the following code.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;push rbp\nmov rbp,rsp\nmov DWORD PTR [rbp-0x4],0x0\nmov eax,0x33\npop rbp\nret\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then export the program &amp;ldquo;File -&amp;gt; Export Program&amp;rdquo;, our patched binary is ready. Now give executable permission and move it to the original directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/move-exported-binary-to-original-directory.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;rename the original&lt;/p&gt;\n&lt;h2 id=&#34;signing-running-the-modded-binary-for-profit&#34;&gt;Signing &amp;amp; Running the modded binary for PROFIT&lt;/h2&gt;\n&lt;p&gt;Unlike windows and linux, macOS won&amp;rsquo;t run applications without signing the binary.. but we can generate your own certificates for personal use (dont have to buy a developer account for 99$/year).&lt;/p&gt;\n&lt;p&gt;create a certificate using &amp;ldquo;keychain Access&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-custom-certifcate.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;choose &amp;ldquo;code signing&amp;rdquo; in &amp;ldquo;certificate type&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-code-signing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Go to the application root &amp;ldquo;/Application/&amp;rdquo; to resign the entire app.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo codesign -s &amp;quot;&amp;lt;name-of-certificate&amp;gt;&amp;quot; -fv CleanMyMac-MAS.app/\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Run the app&amp;hellip; Now its been modded and I don&amp;rsquo;t have to pay 3k INR a year or worry about untrusted binaries from the internet.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/removed-files-beyong-the-limit.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;You can see from the image, all the premium features are now unlocked &amp;amp; we can also remove the junk files exceeding 500MB.&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;This was a fun excerise to dig around. Macos is not that limited as I thought it was&amp;hellip; Sure it has its quirks.&lt;/p&gt;</description></item><item><title>&#34;F-droid&#34; vs the pain that is &#34;Google PlayStore&#34;</title><link>//bluepie.in/2022/01/publishing-to-fdroid-google-play/</link><pubDate>Wed, 05 Jan 2022 21:34:18 +0530</pubDate><guid>//bluepie.in/2022/01/publishing-to-fdroid-google-play/</guid><description>\n&lt;p&gt;&lt;strong&gt;How you doing? hope you are doing well. I missed to post on december because I was juggling too much things at once and needed a break&lt;/strong&gt;&lt;/p&gt;\n&lt;h2 id=&#34;fdroid&#34;&gt;Fdroid?&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://fdroid.org&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Fdroid&lt;/a&gt; is a free and open source Android application repository. You can use Fdroid via the &lt;a href=&#34;https://f-droid.org/client&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;F-Droid client&lt;/a&gt; or the &lt;a href=&#34;https://f-droid.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;F-Droid website&lt;/a&gt;. It is similar to the &lt;a href=&#34;https://play.google.com/store&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Play Store&lt;/a&gt;, but with a few key differences:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;is free and open source&lt;/li&gt;\n&lt;li&gt;contains only open source applications&lt;/li&gt;\n&lt;li&gt;is not a commercial service&lt;/li&gt;\n&lt;li&gt;all google play dependencies are stripped out when publishing&lt;/li&gt;\n&lt;li&gt;goes through a series of automated tests &amp;amp; manual reviews before being accepted&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/fdroid.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;how-do-i-add-a-new-app-to-fdroid&#34;&gt;How do I add a new app to Fdroid?&lt;/h3&gt;\n&lt;p&gt;There are two options, choose one:&lt;/p&gt;\n&lt;p&gt;If you don&amp;rsquo;t want to do it yourself&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;You create a issue in &lt;a href=&#34;https://gitlab.com/fdroid/rfp/-/issues&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;REP - Request For Packaging&lt;/a&gt; and the contributors will review your request and add it to the repository.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/rep.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to do it yourself&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Open a issue on &lt;a href=&#34;https://gitlab.com/fdroid/fdroiddata/-/merge_requests&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Fdroid data&lt;/a&gt; with the appropriate yaml(metadata) file, you should create one with your metadata, refer &lt;a href=&#34;https://f-droid.org/docs/FAQ_-_App_Developers/#how-do-i-get-my-app-included&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this&lt;/a&gt;, &lt;a href=&#34;https://f-droid.org/docs/Submitting_to_F-Droid_Quick_Start_Guide/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this link&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://gitlab.com/fdroid/fdroiddata/blob/master/CONTRIBUTING.md#merge-requests&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this&lt;/a&gt; for more information.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;After being reviewed and added to the repository, it will avialable for everyone to download and install.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/fdroid-data.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;here is a basic example of the metadata yaml file:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;Categories:\nLicense:\nAuthorName:\nAuthorEmail:\nAuthorWebSite:\nWebSite:\nSourceCode:\nIssueTracker:\nChangelog:\n...etc\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;View the full file &lt;a href=&#34;https://gitlab.com/fdroid/wiki/-/wikis/Metadata/YAML-Metadata&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;&lt;/p&gt;\n&lt;h3 id=&#34;how-do-i-publish-updates-to-fdroid&#34;&gt;How do I publish updates to Fdroid?&lt;/h3&gt;\n&lt;p&gt;The great thing about Fdroid is that it automatically detects new versions of your app from github and updates it there. So you just have to make a new tag in your repo and fdroid will handle the rest.&lt;/p&gt;\n&lt;h3 id=&#34;how-did-i-publish-to-fdroid&#34;&gt;How did I publish to Fdroid?&lt;/h3&gt;\n&lt;p&gt;I went through &amp;ldquo;do it yourself&amp;rdquo; and &lt;a href=&#34;https://gitlab.com/fdroid/fdroiddata/-/merge_requests/10097&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this is the pull request&lt;/a&gt; that I created.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;AntiFeatures:\n- NonFreeAdd\nCategories:\n- System\nLicense: GPL-3.0-only\nAuthorName: BluePie\n...etc\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;View the full file is &lt;a href=&#34;https://gitlab.com/bluepie/fdroiddata/-/raw/09cc09572b0352a07b84a01a9d21ae5f4c4a1bc3/metadata/bluepie.ad_silence.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;google-play-store&#34;&gt;Google Play Store?&lt;/h2&gt;\n&lt;p&gt;Goople PlayStore needs no explanation. It is a commercial service that is used by millions of users to download and install apps. It is a great way to monetize &amp;amp; distribute your apps. You need to buy a license with a one time fee though.&lt;/p&gt;\n&lt;p&gt;Anyway lets get started&amp;hellip;&lt;/p&gt;\n&lt;h3 id=&#34;how-do-i-publish-update-to-google-play-store&#34;&gt;How do I publish/Update to Google Play Store?&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;First you create a new app,&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/create-new-app.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Complete all the tasks in the Dashboard&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/complete-tasks-in-console-dashboard.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Generate privacy policy(&lt;a href=&#34;https://app-privacy-policy-generator.firebaseapp.com&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;i recommend this&lt;/a&gt;) and host it somewhere.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/generate-privacy-policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Take build bundle in android studio.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/generate-signed-apk-bundle.png&#34; alt=&#34;&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/generate-bundle-dialog.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Create store listing with appropriate data,&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/update-store-listing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Note to upload the correct images&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/upload-appropriate-images.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;use the bundle from before and make a test release and promote it to production. (review process takes time)&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/upload-bundle.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-pain&#34;&gt;The pain&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Publishing to playstore especially when the app is OpenSource is a manual time consuming process vs the automated process in Fdroid.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;There is a onetime fee for publishing to playstore.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Every release takes a week to get reviewed and published, even if it is OpenSource.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;from my experience time differs between minor, major and bug fixes (based on semantic versioning)&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;The customer support is full of bots&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Store is unfair&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;I got my app delisted because I had the word &amp;ldquo;AD&amp;rdquo; in the Icon.. eventhough there are other apps from playstore with 1m+ installs that also has the same, either way I had to change the Icon to get it back on the store.&lt;/li&gt;\n&lt;li&gt;I could not get a proper response from Support &lt;a href=&#34;https://github.com/aghontpi/ad-silence/issues/19&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;more on here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;The feedback back system is not that great, If users encounter crash/ something is not working.. In github you can have a correct feedback loop but in playstore..its just an uninstall with a lower rating.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/delisted.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;If you have an OpenSource App, spend less time updating it on Playstore. Stick with Github releases or Fdroid releases. Don&amp;rsquo;t even get my started on Apple app store.&lt;/p&gt;</description></item></channel></rss>"}, "7a45a26d99f9294a02e82833d09f0630d3513398": {"t": 1785770796.0298684, "n": 155427, "h": "84b984745ae21e93", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Fri, 03 Jan 2025 18:11:41 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Unlimited Photos Storage Without Root - Part 2</title><link>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</link><pubDate>Fri, 03 Jan 2025 18:11:41 +0000</pubDate><guid>//bluepie.in/2025/01/unlimited-photos-storage-without-root/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;This post is a continuation of my &lt;a href=&#34;https://bluepie.in/2024/04/rooting-my-pixel/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Rooting My Pixel&lt;/a&gt;. Over the past months, I’ve been using my rooted phone to bypass storage limits and enjoy unlimited Google Photos storage. However, using a rooted device isn’t without its challenges—if someone gains physical access to my phone, my data becomes vulnerable. Banking appsa and even general purpose applications refuse to run on rooted devices because they detect system tampering. In some cases, the Play Store won’t allow you to install certain apps at all. To get around these restrictions, I rely on LSPosed modules that help conceal root access, allowing these apps to function without issues.&lt;/p&gt;\n&lt;h2 id=&#34;the-update-dilemma-keeping-root-intact&#34;&gt;The Update Dilemma: Keeping Root Intact&lt;/h2&gt;\n&lt;p&gt;Every time my Pixel receives an update, I’m forced to patch the boot image, a critical step for Magisk to continue functioning properly. The process generally involves:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Downloading the stock image for my Pixel model.&lt;/li&gt;\n&lt;li&gt;Extracting the boot image.&lt;/li&gt;\n&lt;li&gt;Patching the boot image using Magisk.&lt;/li&gt;\n&lt;li&gt;Flashing the patched boot image back onto the device.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Skipping any of these steps means Magisk (and its modules) won’t load, breaking the tweaks I depend on.&lt;/p&gt;\n&lt;h2 id=&#34;why-not-disable-updates&#34;&gt;Why Not Disable Updates?&lt;/h2&gt;\n&lt;p&gt;It might seem tempting to simply disable updates, but the monthly Pixel updates often include important stability and OS fixes. For instance, the December 2024 Android 15 update introduced a bug where Wi-Fi and mobile internet would disconnect automatically after a period of inactivity, a problem that’s been widely discussed on Reddit, Google Forums, and other communities. I’m now waiting for the January patch that should resolve this issue. The takeaway? Staying updated is crucial despite the extra work it sometimes entails.&lt;/p&gt;\n&lt;h2 id=&#34;emulating-the-old-pixel-without-root&#34;&gt;Emulating the Old Pixel Without Root&lt;/h2&gt;\n&lt;p&gt;If you’ve followed my previous posts, you know that rooting was once the only method to emulate the old Pixel’s functionality. Today, patching offers a robust alternative. The &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; provides a comprehensive collection of smali patches that let you modify apps without needing root access.&lt;/p&gt;\n&lt;p&gt;I stumbled upon these patches while exploring alternatives to YouTube Premium. After canceling my subscription.&lt;/p&gt;\n&lt;p&gt;There was some controversy earlier in the ReVanced journey. The project initially faced criticism on GitHub for directly hosting mods, which led to pressure from the google and github. In response, the devs decided to split the project, hosting the patch files separately from the patched binaries. This move not only addressed the concerns but also streamlined the development process, keeping the patch logic distinct and easier to manage.&lt;/p&gt;\n&lt;h2 id=&#34;downloading-the-patches-and-apk&#34;&gt;Downloading the Patches and APK&lt;/h2&gt;\n&lt;p&gt;Before patching, you need to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest Google Photos APK&lt;/strong&gt;&lt;br /&gt;\nMake sure to obtain the standalone APK file rather than a bundled format. sites like &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt; are a good source.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Latest ReVanced Patches&lt;/strong&gt;&lt;br /&gt;\nHead over to the &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;ReVanced Patches repository&lt;/a&gt; to get the most recent version of the patch file (the &lt;code&gt;.rvp&lt;/code&gt; file). Using the latest patches ensures better compatibility with the app version you’re patching.&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h2 id=&#34;patching-the-apk&#34;&gt;Patching the APK&lt;/h2&gt;\n&lt;p&gt;Once you have both the APK and the patch file, you can patch the app using the following command.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;java -jar revanced-cli.jar patch --patches patches.rvp -o patched.apk my_app.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Simply replace &lt;code&gt;our_app.apk&lt;/code&gt; with the path to the downloaded APK (for example, my Google Photos APK). This command tells the Revanced CLI to apply the patches defined in &lt;code&gt;patches.rvp&lt;/code&gt; to my APK and output a patched version named &lt;code&gt;patched.apk&lt;/code&gt;.&lt;/p&gt;\n&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;\n&lt;p&gt;The magic happens in the &lt;code&gt;.rvp&lt;/code&gt; file—it acts as a manifest that lists all supported packages along with their corresponding patches. When you run the command, the tool detects the package name in my APK and applies only the relevant modifications. (A small caveat: the current patching process only works via the PC command-line interface; the Android-based ReVanced Patcher app has issues patching google photos app for years now..)&lt;/p&gt;\n&lt;h2 id=&#34;installing-the-patched-app&#34;&gt;Installing the Patched App&lt;/h2&gt;\n&lt;p&gt;After patching, simply install the new APK on the device. On first launch, the app will prompt you to install ReVanced MicroG—a companion component needed for proper operation. For my convenience, I’m including download links for all the essential files at the end of this post. I’ll update this section as I patch new versions.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img1.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img2.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/jan2025/img3.png&#34; alt=&#34;&#34; style=&#34;height:320px;&#34;&gt;\n&lt;/div&gt;\n&lt;h2 id=&#34;relocking-the-pixel-device&#34;&gt;Relocking the Pixel Device&lt;/h2&gt;\n&lt;p&gt;Relocking Pixel device is a critical step if I want to revert my device back to its original, secure state. Whether we choose to relock via fastboot or by flashing the official ROM, please note that both methods will erase our device data. Make sure to backup all important files before proceeding.&lt;/p&gt;\n&lt;h3 id=&#34;method-1-fastboot-relock&#34;&gt;Method 1: Fastboot Relock&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Fastboot Mode:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Power off device completely.&lt;/li&gt;\n&lt;li&gt;Hold the &lt;strong&gt;Power&lt;/strong&gt; and &lt;strong&gt;Volume Down&lt;/strong&gt; buttons simultaneously until the fastboot screen appears.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relock the Bootloader:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Connect my device to my computer using a USB cable.&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Open a terminal (or Command Prompt) and execute the command:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;fastboot flashing lock\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Some devices might require the command &lt;code&gt;fastboot oem lock&lt;/code&gt; instead.&lt;/p&gt;\n&lt;/blockquote&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable OEM Unlocking:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Once the bootloader is relocked, navigate to &lt;strong&gt;Developer Options&lt;/strong&gt; on the device.&lt;/li&gt;\n&lt;li&gt;Disable the &lt;strong&gt;OEM Unlocking&lt;/strong&gt; option to prevent unauthorized bootloader modifications in the future.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;method-2-flashing-the-official-rom&#34;&gt;Method 2: Flashing the Official ROM&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Official ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Visit the &lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Pixel Devices Site&lt;/a&gt; to download the latest factory image for your specific model.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flash the ROM:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Follow the detailed instructions provided on the support site to flash the official ROM. This method restores the device to its factory state and automatically relocks the bootloader.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Choose the method that best fits your comfort level and requirements. Relocking via fastboot is generally quicker if you’re familiar with command-line operations, while flashing the official ROM provides a comprehensive reset to stock software.&lt;/p&gt;\n&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced CLI&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-cli&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced CLI&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced Patches Repo&lt;/strong&gt;: &lt;a href=&#34;https://github.com/revanced/revanced-patches&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced Patches&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;ReVanced GmsCore&lt;/strong&gt;: &lt;a href=&#34;https://github.com/ReVanced/GmsCore/releases&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GitHub - ReVanced GmsCore&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Google Photos APK&lt;/strong&gt;: &lt;a href=&#34;https://www.apkmirror.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;APKMirror&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;patched-files&#34;&gt;Patched Files&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any future patches that I release will be updated in this section.&lt;/p&gt;\n&lt;h2 id=&#34;patches-files&#34;&gt;Patches Files&lt;/h2&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;File Name&lt;/th&gt;\n&lt;th&gt;Download Link&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 1)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.001&#34;&gt;Download Part 1&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gphotos-patched-jan-2025 (Part 2)&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/gphotos-patched-jan-2025.apk.zip.002&#34;&gt;Download Part 2&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gms-core-jan2025&lt;/td&gt;\n&lt;td&gt;&lt;a href=&#34;//bluepie.in/attachments/jan2025/app.revanced.android.gms-240913008-signed.apk&#34;&gt;Download&lt;/a&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br /&gt;\nThe &lt;code&gt;gphotos-patched-jan-2025&lt;/code&gt; file is split into parts using Keka on macOS with a 99MB split threshold to help manage file sizes and site tracking. After downloading both parts, simply extract the first file (with the &lt;code&gt;.zip.001&lt;/code&gt; extension) using your preferred archive tool (such as Keka, 7-Zip, or WinRAR..etc). The tool will automatically combine the parts and extract the complete APK.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;AI is taking over, wth! For historical matching… DeepSeek is released, competing with OpenAI&amp;rsquo;s latest thinking model. I&amp;rsquo;m sure OpenAI will have some response to it. I’ve enjoyed a 5-day vacation—it was pleasant and something I’ll remember for the upcoming days. (Spent with Vi, aka 3&amp;rsquo;rd Frost.) I&amp;rsquo;m planning to go home and spend some time with family, and I have to set up Jio AirFibre, converting my SIMs to Jio for unlimited internet (you’ll see a post about this later). The broadband over there isn’t that good and the signal inside the home is weak; plus, it comes bundled with TV, movies, and disney+, discovery+,..etc I’m going to disconnect Airtel TV/DTH in favor of this since it works with just WiFi and doesn&amp;rsquo;t require routing everywhere. Since I&amp;rsquo;m planning to vacate Chennai in March, after February (Frost&amp;rsquo;s 3rd birthday), things are going well. I even decided to tell Mom and Dad about Vi and show them by taking them to our Chennai home. Currently, as of this writing, they are not in Chennai with me—they went hometown last time I visited for functions and to take care of Grandma.&lt;/p&gt;</description></item><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can seamlessly automate the issuance and renewal of wildcard certificates. Gone are the days of logging into my DNS provider’s dashboard every few months. With minimal scripting and a few Cron entries, my wildcard certificates can renew without me lifting a finger.&lt;/p&gt;\n&lt;!-- If this guide was helpful, [check out more of my tech insights at BluePie](https://bluepie.in) or leave a comment below to share your experience or ask questions! --&gt;\n&lt;p&gt;Leave a comment below to share your experience or ask questions!&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;p&gt;See you all soon!&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item><item><title>Updating Dns Servers for Ads and Tracking Protection in 2023</title><link>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</link><pubDate>Sat, 28 Jan 2023 06:02:22 +0000</pubDate><guid>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</guid><description>\n&lt;h2 id=&#34;upgrading-pi-hole&#34;&gt;Upgrading PI hole&lt;/h2&gt;\n&lt;p&gt;Once every year, I do server maintainence (during Dec-Jan), this year I updated the our dns server from &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;pi-hole&lt;/a&gt; to &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AGH&lt;/a&gt; (both are open source projects..) Just wanted to try an alternative, after using it for a while, the UI &amp;amp; Features on AGH is much better than pi-hole.&lt;/p&gt;\n&lt;h2 id=&#34;why-a-custom-dns-server&#34;&gt;Why a custom dns server?&lt;/h2&gt;\n&lt;p&gt;All websites/apis are just ip addresses (just numbers). Nameservers maintains a mapping of the domain names to the ip addresses, so when we make a request, we get the appropriate ip address.&lt;/p&gt;\n&lt;p&gt;The idea is simple, by running our own dns server, we can prevent our network providers spying on us, on top of that we can intercept &amp;amp; block all ads &amp;amp; trackers.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;I get ads, but what are Trackers?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Similar to ads, ever been in a situation where you searched for something in flipkart/amazon.. then later you find the ad to buy the product elsewhere, this is because of trackers.. trackers are worse because they collect your data, the same way we block ads, trackers should be blocked.&lt;/p&gt;\n&lt;h2 id=&#34;how-this-can-help-us-with-ad-trackers-blocking&#34;&gt;How this can help us with ad &amp;amp; trackers blocking?&lt;/h2&gt;\n&lt;p&gt;Can you make a guess how serving ads in app/websites work? they are from a server too, that server has a domain linked to it.. can you see where I am are going with this?&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;good content -&amp;gt; respond with ip address\nad/tracker -&amp;gt; respond with 0.0.0.0\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Not just blocking ads &amp;amp; tracking, you can do much more.. there are other things that you can do in dns level.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;block windows tracking or updates in windows machines&lt;/li&gt;\n&lt;li&gt;blocking spyware&lt;/li&gt;\n&lt;li&gt;disabling websites for a particular client (ex: blocking instagram on a your phone alone)&lt;/li&gt;\n&lt;li&gt;if you don&amp;rsquo;t want to keep tracking of your watch history in youtube\nand many more.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;aren-t-there-other-solutions-to-blocking-trackers-and-ads&#34;&gt;Aren&amp;rsquo;t there other solutions to blocking trackers and ads?&lt;/h3&gt;\n&lt;p&gt;yes but those come with a disadvantage,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Browser extensions.. only applies to browsers (system wide, everywhere and all applications)&lt;/li&gt;\n&lt;li&gt;Android, Iphone (separate apps that use vpn locally to achieve this) - Drains battery alot&lt;/li&gt;\n&lt;li&gt;Rooting android, Jailbreaking ios.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;so this does not require any of that.. no addons, no extra apps, no root, no jailbreak.&lt;/p&gt;\n&lt;h2 id=&#34;dns-over-tls&#34;&gt;DNS Over TLS&lt;/h2&gt;\n&lt;p&gt;Introduced in 2016, main idea is wrapping DNS requests and responses via TLS (Transport Layer Security), ensures observer will not be able to track or tamper the contents of the DNS requests.&lt;/p&gt;\n&lt;!-- ssl - secure socket layer --&gt;\n&lt;!-- tls - tranport layer security, offers end to end security. --&gt;\n&lt;p&gt;Shortly after, android introduced DOT as &amp;ldquo;Private DNS&amp;rdquo; on Android 9 &amp;amp; up, we will be using this feature in our devices to block all ADS &amp;amp; Trackers.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/DOT-noMITM.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;client -&amp;gt; dot server, dot server -&amp;gt; queries the dns.. (we forward our request to serveral dns servers, load-balancers)&lt;/p&gt;\n&lt;p&gt;Traditional DNS vs DOT&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/traditional-vs-dot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-do-we-do-it&#34;&gt;How do we do it?&lt;/h2&gt;\n&lt;p&gt;We use an existing open source dns resolver.&lt;/p&gt;\n&lt;p&gt;One options is to go with &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;PI Hole&lt;/a&gt;, it is a community favourite open source project that has all the bells and whistles for blocking ads and trackers integrated inside it.&lt;/p&gt;\n&lt;p&gt;We will be using &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AG Home&lt;/a&gt;, it is also a popular open source project, which is community maintained. up until lastmonth (dec 2022) I was using PI-hole, switched to AG-Home, just to try something new.&lt;/p&gt;\n&lt;h3 id=&#34;installing&#34;&gt;Installing&lt;/h3&gt;\n&lt;p&gt;I am assuming that you already have a linux box hosted in aws/google/..etc. DOT works on port 853, so you have to allow incoming and outgoing traffic in port 853.&lt;/p&gt;\n&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;\n&lt;p&gt;To make things easier, I will be using docker images to run the software.&lt;/p&gt;\n&lt;p&gt;running a docker container.. command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;\npodman run --name dnsServer\\\n--restart unless-stopped\\\n-p 2346:3000/tcp\\\n-p 1853:853/tcp\\\n-d adguard/adguardhome\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Things to note are, exposed port 3000 is the webport for admin page &amp;amp; 853 is for DOT.&lt;/p&gt;\n&lt;p&gt;We maintain the configs in a yaml file which will be mounted to the container.. here is an example version.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;bind_host: 0.0.0.0\nbind_port: 3000\nbeta_bind_port: 0\nusers:\n# - name: &amp;lt;your-username&amp;gt;\n# password: &amp;lt;your-passowrd&amp;gt;\nauth_attempts: 5\nblock_auth_min: 15\nhttp_proxy: &amp;quot;&amp;quot;\nlanguage: &amp;quot;&amp;quot;\ntheme: auto\ndebug_pprof: false\nweb_session_ttl: 720\ndns:\nbind_hosts:\n- 0.0.0.0\nblocked_response_ttl: 10\nratelimit: 20\nrefuse_any: true\nfastest_timeout: 1s\ntls:\nenabled: true\nserver_name: dns.bluepie.in\nport_https: 443\nport_dns_over_tls: 853\ncertificate_path: /opt/dnsServer/conf/fullchain.pem\nprivate_key_path: /opt/dnsServer/conf/privkey.pem\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once everything is configured correctly, the service will be up and running.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman ps\nb831cf04743d docker.io/adguard/adguardhome:latest --no-check-update... 1 days ago Up 3 weeks ago 0.0.0.0:1853-&amp;gt;853/tcp, 0.0.0.0:2346-&amp;gt;3000/tcp dnsServer\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;configuring-domain&#34;&gt;Configuring Domain&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;First we need a domain name, a paid one if you have it or a free one would be good, I&amp;rsquo;ll be creating a sub-domain from my domain, (i.e) &amp;lsquo;dns.bluepie.in&amp;rsquo; and get SSL certificates chain for the same.&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Inorder to uniquely identify clients, we need to create a wildcard sud-domain in our DNS provider, in my case godaddy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/creating-wild-card-dns.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h4 id=&#34;nginx&#34;&gt;Nginx&lt;/h4&gt;\n&lt;p&gt;We will be using nginx as a reverse proxy for exposing our services to the internet.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Admin Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server_name dns.bluepie.in;\nlocation / {\nproxy_pass http://localhost:2346/;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_redirect / / ;\nproxy_cookie_path / /;\nproxy_connect_timeout 15s;\nproxy_send_timeout 15s;\nproxy_read_timeout 15s;\n}\nlisten 443 ssl;\nssl_certificate /path/to/fullchain.pem;\nssl_certificate_key /path/to/privkey.pem;\ninclude /etc/ssl/options-ssl-nginx.conf;\nssl_dhparam /etc/ssl/ssl-dhparams.pem;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;DNS Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;nginx has a load balancing feature, stream directive to manage tcp and udp. we can use this to forward our requests to dns servers.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;stream {\nlog_format basic &#39;$time_iso8601 $remote_addr &#39;\n&#39;$protocol $status $bytes_sent $bytes_received &#39;\n&#39;$session_time $upstream_addr &#39;\n&#39;&amp;quot;$upstream_bytes_sent&amp;quot; &amp;quot;$upstream_bytes_received&amp;quot; &amp;quot;$upstream_connect_time&amp;quot;&#39;;\naccess_log /var/log/nginx/dns-access.log basic buffer=1k flush=5s;\nupstream dot {\nzone dot 512k;\nserver 127.0.0.1:1853;\n}\nserver {\nlisten 853;\nproxy_pass dot;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;admin-portal&#34;&gt;Admin portal&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;Stats page (configured to refresh every 24 hours)&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-statistics.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Configuring blocklists&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Over the years, I have accustomed to the following blocklists and it works best for me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-blocklists.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Blocklists (entires count taken at the time of writing this post&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# Blocks Ads, Mobile Ads, Phishing, Malvertising, Malware, Tracking, Telemetry, CryptoJacking, Analytics, Spyware, Ransomware, Exploit, Fraud, Abuse, Scam, Spam, Hijack, Misleading Marketing.\n# ~300,000 entries\nhttps://oisd.nl\n# ~180,000 entries\n# Extending and consolidating hosts files from several well-curated sources like adaway.org, mvps.org, malwaredomainlist.com, someonewhocares.org, and potentially others.\ngithub.com/StevenBlack/hosts\n# ~480,000 entries\n# Automatically updated, moderated and optimized list for blocking ads, trackers and other online garbage.\ngithub.com/notracking/hosts-blocklists\n# Specially Designed for Mobile Ad Protection.\n# ~200,000 entries\ngithub.com/jerryn70/GoodbyeAds\n# Blocking mobile ad providers and some analytics providers.\n# ~7,000 entries\ngithub.com/AdAway/adaway.github.io\n# A filter composed of several other filters (AdGuard Base filter, Social media filter, Tracking Protection filter, Mobile Ads filter, EasyList and EasyPrivacy) and simplified specifically to be better compatible with DNS-level ad blocking.\n# ~50,000 entries\ngithub.com/AdguardTeam/AdguardSDNSFilter\n# Protects you from many types of spyware, reduces bandwidth use, blocks certain pop-up traps, prevents user tracking by way of &amp;quot;web bugs&amp;quot; embedded in spam, provides partial protection to IE from certain web-based exploits and blocks most advertising you would otherwise be subjected to on the internet.\n# ~11,000 entries\nsomeonewhocares.org/hosts\n# Filter that blocks ads on mobile devices. Contains all known mobile ad networks.\n# ~1,000 entries\nkb.adguard.com/general/adguard-ad-filters#mobile-ads-filter\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-do-we-use-it&#34;&gt;How do we use it?&lt;/h2&gt;\n&lt;p&gt;Pick a unique word, ex: gopidevice1, then use this word as prefix for &amp;lsquo;dns.bluepie.in&amp;rsquo;. (i.e) &amp;lsquo;gopidevice1.dns.bluepie.in&amp;rsquo;, this is the url that you should use in your devices.&lt;/p&gt;\n&lt;h3 id=&#34;android&#34;&gt;Android&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/android-config.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;ios-iphone-ipad&#34;&gt;IOS - iphone, ipad&lt;/h3&gt;\n&lt;p&gt;Go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;, navigate to &amp;lsquo;tool&amp;rsquo;, give a name and use your unique url to create a profile.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;mac-windows&#34;&gt;Mac/Windows&lt;/h3&gt;\n&lt;p&gt;Same as iphone, go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dot-install-macos-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Migrating from PI-Hole to AGH was a good move, once you have used this and experienced &amp;ldquo;no ads&amp;rdquo;, you can not go back~&lt;/p&gt;</description></item><item><title>Modding a macOS App</title><link>//bluepie.in/2022/02/modding-a-macos-app/</link><pubDate>Sun, 20 Feb 2022 19:10:13 +0000</pubDate><guid>//bluepie.in/2022/02/modding-a-macos-app/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;This post is about reversing.. but for a MACOS binary. Lets drive straight into it.&lt;/p&gt;\n&lt;p&gt;Modding in macOS is fairly new to me, but a binary is a binary and it can be modded!. I started to daily drive macOS a month or two ago, the why and what&amp;rsquo;s is a whole different story to post about.&lt;/p&gt;\n&lt;p&gt;Like I have said previously in my other &lt;a href=&#34;https://bluepie.in/categories/reversing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;re posts&lt;/a&gt;, &amp;ldquo; I don&amp;rsquo;t trust mods from the internet&amp;rdquo; If I can mod it then its fine, otherwise I&amp;rsquo;m paying for it.&lt;/p&gt;\n&lt;p&gt;I just started to understand the quirks of &amp;ldquo;OS X&amp;rdquo;, Though some of my methods may be crude&amp;hellip;it works, I will perfect them..&lt;/p&gt;\n&lt;h2 id=&#34;target&#34;&gt;Target&lt;/h2&gt;\n&lt;p&gt;So there is this great app for cleaning your Mac&amp;rsquo;s junk/storage files, but the free version only lets you clean 500mb daily.. the premium version is a whooping 3k per year&amp;hellip;do you get where I am going with this ? &amp;hellip;YES!&amp;hellip;modding for fun and profit (PROFIT = personal use)&lt;/p&gt;\n&lt;h2 id=&#34;tools&#34;&gt;Tools&lt;/h2&gt;\n&lt;p&gt;So I&amp;rsquo;m gonna be trying out ghidra &amp;ldquo; The NSA&amp;rsquo;s reverse engineering tool &amp;ldquo;, mainly because Its a open source tool and I wanted to do some reverse engineering with it for a long time now.&lt;/p&gt;\n&lt;p&gt;make sure you have &lt;a href=&#34;https://brew.sh/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;brew&lt;/a&gt; installed and then run the following command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-console&#34;&gt;brew install --cask ghidra\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;making-observation-to-the-target&#34;&gt;Making observation to the target&lt;/h2&gt;\n&lt;p&gt;The app throws you with the &amp;ldquo;Nag Screen&amp;rdquo; whenever you use the app, it blocks the usage of features (can not use the app to remove more than 500MB of data,privacy scanning won&amp;rsquo;t work unless you pay,..etc).&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;500mb storage limit reached &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/500mb-over-nag-screen.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Trial period expired &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/clean-my-mac-unmodded.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;basics-understanding-ghidra&#34;&gt;Basics &amp;amp; Understanding Ghidra&lt;/h2&gt;\n&lt;p&gt;All macOS apps have the following structure, the main binary will always be inside the MacOS directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/mac-app-folder-structure.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Launch Ghidra and create a new project, then drag and drop the binary into the project.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-new-project.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Mac has this binary format &amp;ldquo;macobinary&amp;rdquo; - basically it contains the different target executables, ex: (arm binary vs the x86 binary).. mine is a x86 machine, will be modding that alone.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-appropriate-target-binary.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;After the import, Ghidra will automatically ask you to analyse the target binary, choose Analyse. (it will make our life easier), based on the binary size and your cpu cores, it may take couple minutes.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/will-ask-for-analyse-choose-analyse.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Only focus on the following five windows, that is enough to get the job done.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/ghidra-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;1 = will contain the symbol tabel, this is where we do most of the filtering.&lt;/li&gt;\n&lt;li&gt;2 = shows the disassembly of the binary (assembly language), this is where we do the actual reversing, (i.e) patching, understanding logic, etc.&lt;/li&gt;\n&lt;li&gt;3 = This is a great feature, shows the equivalent c code, to the selected assembly section.&lt;/li&gt;\n&lt;li&gt;4 &amp;amp; 5 = shows the incoming and outgoing function calls, this will help us find interesting things about the binary, which then can be used for identifying logic and patching.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;IF ANY OF THE ABOVE WINDOWS ARE NOT OPEN, YOU CAN ENABLE THEM THROUGH THE MENU OPTION &amp;ldquo;WINDOW&amp;rdquo;.&lt;/p&gt;\n&lt;h2 id=&#34;analysis&#34;&gt;Analysis&lt;/h2&gt;\n&lt;p&gt;It will take some time to find what you are looking for, but I have already invested time in this, so what we need is &amp;ldquo;activationManager&amp;rdquo; class, it has all sorts of interesting functions like &amp;ldquo;activationStatus, isAppActivated..etc&amp;rdquo;. Looking for references, it is used throughout the app, so lets change it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/different-app-status.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Looking around, we find the different status codes inside &amp;ldquo;activationStatus&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;By checking other functions, it becomes obvious that, changing only &amp;ldquo;isAppActivated&amp;rdquo; function is enough. The return type of the function is &amp;ldquo;char&amp;rdquo;, with reference to &amp;ldquo;status&amp;rdquo; from above..we should return &amp;ldquo;3&amp;rdquo;. From the ascii table, &amp;ldquo;0x33&amp;rdquo; is the equivalnet for &amp;ldquo;3&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;Patching the &amp;ldquo;isAppActivated&amp;rdquo; function, with the following code.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;push rbp\nmov rbp,rsp\nmov DWORD PTR [rbp-0x4],0x0\nmov eax,0x33\npop rbp\nret\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then export the program &amp;ldquo;File -&amp;gt; Export Program&amp;rdquo;, our patched binary is ready. Now give executable permission and move it to the original directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/move-exported-binary-to-original-directory.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;rename the original&lt;/p&gt;\n&lt;h2 id=&#34;signing-running-the-modded-binary-for-profit&#34;&gt;Signing &amp;amp; Running the modded binary for PROFIT&lt;/h2&gt;\n&lt;p&gt;Unlike windows and linux, macOS won&amp;rsquo;t run applications without signing the binary.. but we can generate your own certificates for personal use (dont have to buy a developer account for 99$/year).&lt;/p&gt;\n&lt;p&gt;create a certificate using &amp;ldquo;keychain Access&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-custom-certifcate.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;choose &amp;ldquo;code signing&amp;rdquo; in &amp;ldquo;certificate type&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-code-signing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Go to the application root &amp;ldquo;/Application/&amp;rdquo; to resign the entire app.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo codesign -s &amp;quot;&amp;lt;name-of-certificate&amp;gt;&amp;quot; -fv CleanMyMac-MAS.app/\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Run the app&amp;hellip; Now its been modded and I don&amp;rsquo;t have to pay 3k INR a year or worry about untrusted binaries from the internet.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/removed-files-beyong-the-limit.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;You can see from the image, all the premium features are now unlocked &amp;amp; we can also remove the junk files exceeding 500MB.&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;This was a fun excerise to dig around. Macos is not that limited as I thought it was&amp;hellip; Sure it has its quirks.&lt;/p&gt;</description></item><item><title>&#34;F-droid&#34; vs the pain that is &#34;Google PlayStore&#34;</title><link>//bluepie.in/2022/01/publishing-to-fdroid-google-play/</link><pubDate>Wed, 05 Jan 2022 21:34:18 +0530</pubDate><guid>//bluepie.in/2022/01/publishing-to-fdroid-google-play/</guid><description>\n&lt;p&gt;&lt;strong&gt;How you doing? hope you are doing well. I missed to post on december because I was juggling too much things at once and needed a break&lt;/strong&gt;&lt;/p&gt;\n&lt;h2 id=&#34;fdroid&#34;&gt;Fdroid?&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://fdroid.org&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Fdroid&lt;/a&gt; is a free and open source Android application repository. You can use Fdroid via the &lt;a href=&#34;https://f-droid.org/client&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;F-Droid client&lt;/a&gt; or the &lt;a href=&#34;https://f-droid.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;F-Droid website&lt;/a&gt;. It is similar to the &lt;a href=&#34;https://play.google.com/store&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Play Store&lt;/a&gt;, but with a few key differences:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;is free and open source&lt;/li&gt;\n&lt;li&gt;contains only open source applications&lt;/li&gt;\n&lt;li&gt;is not a commercial service&lt;/li&gt;\n&lt;li&gt;all google play dependencies are stripped out when publishing&lt;/li&gt;\n&lt;li&gt;goes through a series of automated tests &amp;amp; manual reviews before being accepted&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/fdroid.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;how-do-i-add-a-new-app-to-fdroid&#34;&gt;How do I add a new app to Fdroid?&lt;/h3&gt;\n&lt;p&gt;There are two options, choose one:&lt;/p&gt;\n&lt;p&gt;If you don&amp;rsquo;t want to do it yourself&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;You create a issue in &lt;a href=&#34;https://gitlab.com/fdroid/rfp/-/issues&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;REP - Request For Packaging&lt;/a&gt; and the contributors will review your request and add it to the repository.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/rep.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to do it yourself&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Open a issue on &lt;a href=&#34;https://gitlab.com/fdroid/fdroiddata/-/merge_requests&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Fdroid data&lt;/a&gt; with the appropriate yaml(metadata) file, you should create one with your metadata, refer &lt;a href=&#34;https://f-droid.org/docs/FAQ_-_App_Developers/#how-do-i-get-my-app-included&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this&lt;/a&gt;, &lt;a href=&#34;https://f-droid.org/docs/Submitting_to_F-Droid_Quick_Start_Guide/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this link&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://gitlab.com/fdroid/fdroiddata/blob/master/CONTRIBUTING.md#merge-requests&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this&lt;/a&gt; for more information.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;After being reviewed and added to the repository, it will avialable for everyone to download and install.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/fdroid-data.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;here is a basic example of the metadata yaml file:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;Categories:\nLicense:\nAuthorName:\nAuthorEmail:\nAuthorWebSite:\nWebSite:\nSourceCode:\nIssueTracker:\nChangelog:\n...etc\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;View the full file &lt;a href=&#34;https://gitlab.com/fdroid/wiki/-/wikis/Metadata/YAML-Metadata&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;&lt;/p&gt;\n&lt;h3 id=&#34;how-do-i-publish-updates-to-fdroid&#34;&gt;How do I publish updates to Fdroid?&lt;/h3&gt;\n&lt;p&gt;The great thing about Fdroid is that it automatically detects new versions of your app from github and updates it there. So you just have to make a new tag in your repo and fdroid will handle the rest.&lt;/p&gt;\n&lt;h3 id=&#34;how-did-i-publish-to-fdroid&#34;&gt;How did I publish to Fdroid?&lt;/h3&gt;\n&lt;p&gt;I went through &amp;ldquo;do it yourself&amp;rdquo; and &lt;a href=&#34;https://gitlab.com/fdroid/fdroiddata/-/merge_requests/10097&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this is the pull request&lt;/a&gt; that I created.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;AntiFeatures:\n- NonFreeAdd\nCategories:\n- System\nLicense: GPL-3.0-only\nAuthorName: BluePie\n...etc\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;View the full file is &lt;a href=&#34;https://gitlab.com/bluepie/fdroiddata/-/raw/09cc09572b0352a07b84a01a9d21ae5f4c4a1bc3/metadata/bluepie.ad_silence.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;google-play-store&#34;&gt;Google Play Store?&lt;/h2&gt;\n&lt;p&gt;Goople PlayStore needs no explanation. It is a commercial service that is used by millions of users to download and install apps. It is a great way to monetize &amp;amp; distribute your apps. You need to buy a license with a one time fee though.&lt;/p&gt;\n&lt;p&gt;Anyway lets get started&amp;hellip;&lt;/p&gt;\n&lt;h3 id=&#34;how-do-i-publish-update-to-google-play-store&#34;&gt;How do I publish/Update to Google Play Store?&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;First you create a new app,&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/create-new-app.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Complete all the tasks in the Dashboard&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/complete-tasks-in-console-dashboard.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Generate privacy policy(&lt;a href=&#34;https://app-privacy-policy-generator.firebaseapp.com&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;i recommend this&lt;/a&gt;) and host it somewhere.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/generate-privacy-policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Take build bundle in android studio.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/generate-signed-apk-bundle.png&#34; alt=&#34;&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/generate-bundle-dialog.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Create store listing with appropriate data,&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/update-store-listing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Note to upload the correct images&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/upload-appropriate-images.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;use the bundle from before and make a test release and promote it to production. (review process takes time)&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/upload-bundle.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-pain&#34;&gt;The pain&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Publishing to playstore especially when the app is OpenSource is a manual time consuming process vs the automated process in Fdroid.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;There is a onetime fee for publishing to playstore.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Every release takes a week to get reviewed and published, even if it is OpenSource.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;from my experience time differs between minor, major and bug fixes (based on semantic versioning)&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;The customer support is full of bots&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Store is unfair&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;I got my app delisted because I had the word &amp;ldquo;AD&amp;rdquo; in the Icon.. eventhough there are other apps from playstore with 1m+ installs that also has the same, either way I had to change the Icon to get it back on the store.&lt;/li&gt;\n&lt;li&gt;I could not get a proper response from Support &lt;a href=&#34;https://github.com/aghontpi/ad-silence/issues/19&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;more on here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;The feedback back system is not that great, If users encounter crash/ something is not working.. In github you can have a correct feedback loop but in playstore..its just an uninstall with a lower rating.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/delisted.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;If you have an OpenSource App, spend less time updating it on Playstore. Stick with Github releases or Fdroid releases. Don&amp;rsquo;t even get my started on Apple app store.&lt;/p&gt;</description></item><item><title>Accuradio, Spotify...etc Adblocker in Kotlin</title><link>//bluepie.in/2021/11/accuradio-spotify-adblocker-in-kotlin/</link><pubDate>Thu, 18 Nov 2021 19:46:00 +0000</pubDate><guid>//bluepie.in/2021/11/accuradio-spotify-adblocker-in-kotlin/</guid><description>\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;Hello how you been?, hope everything is going well.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;I wanted to learn Kotlin for native Android development, I already knew how to do it with java, so the only obstacle for me is learning Kotlin.&lt;/p&gt;\n&lt;h2 id=&#34;kotlin&#34;&gt;Kotlin&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;Kotlin &lt;a href=&#34;https://trello.com/c/zYFtp51G/1-learn-kotlin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;trello board&lt;/a&gt; that I used to track progress.&lt;/li&gt;\n&lt;li&gt;After learning the basics for Kotlin, &lt;a href=&#34;https://play.kotlinlang.org/koans/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Kotlin koans&lt;/a&gt; is a great place for practice (beginner to advanced).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;chosing-what-to-do-with-kotlin&#34;&gt;Chosing what to do with Kotlin&lt;/h2&gt;\n&lt;p&gt;I use &lt;a href=&#34;https://accuradio.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Accuradio&lt;/a&gt; android app to stream music, but it does not have a paid tear, So the ads are not skippable and there was no existing adblocker that does this(which is a pain). So I decided to write one in Kotlin.&lt;/p&gt;\n&lt;p&gt;So its decided, I am doing a project in Kotlin to block/silence ads in Accuradio, some additional things I decided to implement are the following.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;should be extensible to other apps like Spotify,&amp;hellip;etc&lt;/li&gt;\n&lt;li&gt;should have minimal UI&lt;/li&gt;\n&lt;li&gt;should be lightweight&lt;/li&gt;\n&lt;li&gt;should be completely openSource.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;To be extensbile, I should have a common pattern/way to detect ads in these apps&amp;hellip; One such way is by using app&amp;rsquo;s notification.&lt;/p&gt;\n&lt;p&gt;I can determine what an app is currenly playing using the notification, using the &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;notification listener&lt;/a&gt; service and parsing the posted notifications, mute/unmute the app based on the content.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/accuradio-spotify-ad-blocker/flow.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;An update, &lt;strong&gt;I already added support for Spotify and Tidal.&lt;/strong&gt;&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;implementation&#34;&gt;Implementation&lt;/h2&gt;\n&lt;p&gt;I pretty much have to just&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;implement the &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;NotificationListenerService&lt;/a&gt; service, and parse the posted notifications,&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;extend the &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;NotificationListenerService&lt;/a&gt; class and implement the &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService.OnNotificationPostedListener.html#onNotificationPosted&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;onNotificationPosted&lt;/a&gt; method &amp;amp; &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService.OnNotificationConnectedListener.html#onNotificationConnected&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;onNotificationConnected&lt;/a&gt; method.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-kt&#34;&gt;class NotificationListener : NotificationListenerService() {\noverride fun onListenerConnected() {\nsuper.onListenerConnected()\nappNotificationHelper?.updateNotification(&amp;quot;AdSilence, listening for ads&amp;quot;)?.run {\nstartForeground(NOTIFICATION_ID, this) // persistent notification\n}\nLog.v(TAG, &amp;quot;notification listener connected&amp;quot;)\n}\noverride fun onNotificationPosted(sbn: StatusBarNotification?) {\nsuper.onNotificationPosted(sbn)\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;why do I need to run it as a foreground service?&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;because In recent versions of android, any app that is actively running the background must post a notification to the status bar. Otherwise the app will be killed by the system.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;then mute/unmute the app based on the content,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-kt&#34;&gt;\nsbn?.let {\nwith(AppNotification(applicationContext, it.notification, sbn.packageName)) {\nPreference(applicationContext).isAppConfigured(this.getApp()).takeIf { b -&amp;gt; b }\n?.run {\nUtils().run {\nwhen (NotificationParser(this@with).isAd()) {\ntrue -&amp;gt; {\nthis.mute()\nisMuted = true\n}\nfalse -&amp;gt; {\nisMuted = false\nthis.unmute();\n)\n}\n}\n}\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;next important step is granting the app, the permission to listen to notifications. (if all apps can listen to incoming notifications, this would be a huge security flaw!)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://developer.android.com/training/permissions/requesting&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;grant permission&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;since this app is openSource, everyone can see the source code before granting permission&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;bascially the following one line has to be used. (note: using string to support older devices)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-kotlin&#34;&gt;startActivity(Intent(&amp;quot;enabled_notification_listeners&amp;quot;))\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;in the android manifest, add the following to the &lt;code&gt;&amp;lt;application&amp;gt;&lt;/code&gt; tag,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;telling the system to use the file &lt;code&gt;NotificationListenerService&lt;/code&gt; as the notification listener service.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;service android:name=&amp;quot;.NotificationListener&amp;quot;\nandroid:label=&amp;quot;@string/notification_listener_name&amp;quot;\nandroid:permission=&amp;quot;android.permission.BIND_NOTIFICATION_LISTENER_SERVICE&amp;quot;\nandroid:exported=&amp;quot;false&amp;quot;&amp;gt;\n&amp;lt;intent-filter&amp;gt;\n&amp;lt;action android:name=&amp;quot;android.service.notification.NotificationListenerService&amp;quot; /&amp;gt;\n&amp;lt;/intent-filter&amp;gt;\n&amp;lt;/service&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;That is all is required to get the app to work, there are other things like UI, checking app installed or not, maintaining the app running in the background, hiding the app from recents, restarting the service, preference&amp;hellip;etc. Since the app is so lightweight (around 150KB)&amp;hellip;&lt;/p&gt;\n&lt;p&gt;I could write about every part of the app in this, but this app is open source&amp;hellip;.its already explained. It was so refreshing and fun work on this&amp;hellip; Kotlin is fun and I am glad to have learned it(atleast some bit).&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Github/source code&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://play.google.com/store/apps/details?id=bluepie.ad_silence&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google play store link&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://f-droid.org/packages/bluepie.ad_silence/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Fdroid link&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://youtu.be/hWfiQN4IUFM&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;featured in top apps - youtube&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.nextpit.com/apps-of-the-week-48-2021&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;featured in app of the week&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;</description></item></channel></rss>"}, "dec7fcb68a93a95a28fad12580981d5523b85ef1": {"t": 1785770799.815202, "n": 156136, "h": "e4d76f4ee98a61b8", "c": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Bluepie&#39;s Journal on Bluepie&#39;s Journal</title><link>//bluepie.in/</link><description>Recent content in Bluepie&#39;s Journal on Bluepie&#39;s Journal</description><language>en-us</language><lastBuildDate>Sat, 07 Dec 2024 03:02:36 +0000</lastBuildDate><atom:link href=\"/\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Certbot Manual Hook – Automating Wildcard Domain Renewals</title><link>//bluepie.in/2024/12/certbot-manual-hook/</link><pubDate>Sat, 07 Dec 2024 03:02:36 +0000</pubDate><guid>//bluepie.in/2024/12/certbot-manual-hook/</guid><description>\n&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;\n&lt;p&gt;Securing my website with HTTPS is crucial, and &lt;a href=&#34;https://certbot.eff.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Certbot&lt;/a&gt; makes it straightforward for many domains. However, when it comes to wildcard domains (like &lt;code&gt;*.domain.com&lt;/code&gt;), there’s a bit more work involved—particularly if I want to automate renewals.&lt;/p&gt;\n&lt;p&gt;This post explains how I set up a &lt;strong&gt;manual DNS hook&lt;/strong&gt; for Certbot that integrates with my domain name provider’s API (in this example, GoDaddy), so I no longer have to manually add DNS TXT records every time I need to renew my wildcard certificate.&lt;/p&gt;\n&lt;h2 id=&#34;why-do-we-need-this&#34;&gt;Why Do We Need This?&lt;/h2&gt;\n&lt;p&gt;Certbot automates the HTTPS setup for many domain configurations. However, &lt;strong&gt;wildcard certificates&lt;/strong&gt; require DNS-based validation—meaning I must add a TXT record to prove domain ownership. Certbot does not automatically update DNS for every provider. Thus, without automation, I’d be forced to:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Grab the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record details.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Log in to my DNS panel and manually create the TXT record.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Wait for DNS to propagate.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Resume the Certbot process.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Finally, clean up the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;Doing this every few months quickly becomes tedious, especially if I manage multiple wildcard domains. Luckily, Certbot’s &lt;code&gt;--manual-auth-hook&lt;/code&gt; parameter can help me automate these steps.&lt;/p&gt;\n&lt;h2 id=&#34;the-goal&#34;&gt;The Goal&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Automate&lt;/strong&gt; the DNS validation process for wildcard certificate renewals using Certbot hooks.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Utilize my DNS provider’s API (in my case, &lt;strong&gt;GoDaddy&lt;/strong&gt;) to create and remove the required &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record automatically.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Integrate this process into a &lt;code&gt;crontab&lt;/code&gt; so renewals happen on schedule without manual intervention.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;current-manual-process&#34;&gt;Current (Manual) Process&lt;/h2&gt;\n&lt;p&gt;Here’s what I was doing without automation:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Run Certbot: &lt;code&gt;certbot certonly --manual ...&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Certbot tells me which TXT record to create, e.g. &lt;code&gt;&amp;quot;_acme-challenge.domain.com&amp;quot;&lt;/code&gt;.&lt;/li&gt;\n&lt;li&gt;I log into GoDaddy (or another provider) and create the TXT record with the provided value.&lt;/li&gt;\n&lt;li&gt;I wait for DNS propagation (a few minutes).&lt;/li&gt;\n&lt;li&gt;Certbot verifies and finalizes the certificate issuance.&lt;/li&gt;\n&lt;li&gt;I remove or empty out the TXT record.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;This process, while doable, is time-consuming and must be repeated every 60–90 days (depending on my renewal schedule).&lt;/p&gt;\n&lt;h2 id=&#34;godaddy-api-endpoint&#34;&gt;Godaddy API endpoint&lt;/h2&gt;\n&lt;h2 id=&#34;automating-the-workflow&#34;&gt;Automating the Workflow&lt;/h2&gt;\n&lt;p&gt;To automate the renewal of my wildcard domain certificate, I leverage my domain provider&amp;rsquo;s API to manage DNS TXT records programmatically. I use &lt;strong&gt;GoDaddy&lt;/strong&gt;.&lt;/p&gt;\n&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;strong&gt;API Access:&lt;/strong&gt; Ensure GoDaddy supports DNS management via API.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;API Credentials:&lt;/strong&gt; Obtain my API key and secret from the &lt;a href=&#34;https://developer.godaddy.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;GoDaddy Developer Portal&lt;/a&gt;.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;h3 id=&#34;godaddy-api-endpoint-1&#34;&gt;GoDaddy API Endpoint&lt;/h3&gt;\n&lt;p&gt;I use the following endpoint to manage TXT records:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${GODADDY_DOMAIN}&lt;/code&gt;&lt;/strong&gt;: My root domain (e.g., &lt;code&gt;example.com&lt;/code&gt;).&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;${RECORD_NAME}&lt;/code&gt;&lt;/strong&gt;: Typically &lt;code&gt;_acme-challenge&lt;/code&gt; for Certbot.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;using-the-api&#34;&gt;Using the API&lt;/h3&gt;\n&lt;h4 id=&#34;testing-create-update-txt-record-endpoints&#34;&gt;Testing Create/Update TXT Record Endpoints&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: 600}]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;Authorization&lt;/code&gt;: &lt;code&gt;sso-key MY_API_KEY:MY_API_SECRET&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;data&amp;quot;&lt;/code&gt;: Validation string from Certbot.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;&amp;quot;ttl&amp;quot;&lt;/code&gt;: Time-to-live (e.g., &lt;code&gt;600&lt;/code&gt; seconds).&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h4 id=&#34;remove-txt-record&#34;&gt;Remove TXT Record&lt;/h4&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;curl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;[]&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;integration-with-certbot-hooks&#34;&gt;Integration with Certbot Hooks&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Auth Hook (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;):&lt;/strong&gt; Adds the TXT record before validation.&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Cleanup Hook (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;):&lt;/strong&gt; Removes the TXT record after validation.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I ensure my scripts use the above &lt;code&gt;curl&lt;/code&gt; commands with the appropriate environment variables (&lt;code&gt;GODADDY_KEY&lt;/code&gt;, &lt;code&gt;GODADDY_SECRET&lt;/code&gt;, &lt;code&gt;GODADDY_DOMAIN&lt;/code&gt;, &lt;code&gt;RECORD_NAME&lt;/code&gt;, &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;).&lt;/p&gt;\n&lt;h2 id=&#34;the-automated-solution&#34;&gt;The Automated Solution&lt;/h2&gt;\n&lt;p&gt;Certbot offers hooks that run custom scripts to automate these exact steps:&lt;/p&gt;\n&lt;h3 id=&#34;1-command-with-hooks&#34;&gt;1. Command with Hooks&lt;/h3&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;certbot certonly \\\n--manual \\\n--preferred-challenges dns \\\n--manual-auth-hook /etc/letsencrypt/scripts/godaddy-auth.sh \\\n--manual-cleanup-hook /etc/letsencrypt/scripts/godaddy-cleanup.sh \\\n--agree-tos \\\n-d &#39;*.wild.card.domain&#39; \\\n--preferred-chain &amp;quot;ISRG Root X1&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-auth-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script before Certbot attempts to validate the domain (to set up the TXT record).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;&lt;code&gt;--manual-cleanup-hook&lt;/code&gt;&lt;/strong&gt;: Runs a script after validation completes (to remove or clear the TXT record).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;2-godaddy-auth-script&#34;&gt;2. GoDaddy Auth Script&lt;/h3&gt;\n&lt;p&gt;The following script (&lt;code&gt;godaddy-auth.sh&lt;/code&gt;) is triggered by the &lt;code&gt;--manual-auth-hook&lt;/code&gt;. It reads environment variables set by Certbot—like &lt;code&gt;CERTBOT_DOMAIN&lt;/code&gt; and &lt;code&gt;CERTBOT_VALIDATION&lt;/code&gt;—and then updates the DNS record via the GoDaddy API.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-auth.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\n# - CERTBOT_VALIDATION (the validation string for TXT record)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot; # e.g., &amp;quot;example.com&amp;quot;\nTTL=600\n# Subdomain that needs to be updated\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Build request payload\nDATA=&amp;quot;[{\\&amp;quot;data\\&amp;quot;: \\&amp;quot;${CERTBOT_VALIDATION}\\&amp;quot;, \\&amp;quot;ttl\\&amp;quot;: ${TTL}}]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy AUTH-HOOK: Created TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n# Sleep to allow DNS propagation\nsleep 60\n&lt;/code&gt;&lt;/pre&gt;\n&lt;blockquote&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If I have a subdomain or different domain structure, I adjust &lt;code&gt;RECORD_NAME&lt;/code&gt; accordingly.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h3 id=&#34;3-godaddy-cleanup-script&#34;&gt;3. GoDaddy Cleanup Script&lt;/h3&gt;\n&lt;p&gt;This script (&lt;code&gt;godaddy-cleanup.sh&lt;/code&gt;) runs after verification succeeds. It removes (or resets) the &lt;code&gt;_acme-challenge&lt;/code&gt; TXT record, keeping my DNS tidy.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/usr/bin/env bash\n# /etc/letsencrypt/scripts/godaddy-cleanup.sh\n# Certbot environment variables:\n# - CERTBOT_DOMAIN (the domain being authenticated)\nGODADDY_KEY=&amp;quot;domain_name_provider_key&amp;quot;\nGODADDY_SECRET=&amp;quot;domain_name_provider_secret&amp;quot;\nGODADDY_DOMAIN=&amp;quot;your_root_domain&amp;quot;\nRECORD_NAME=&amp;quot;_acme-challenge&amp;quot;\n# Empty the TXT record (or remove it altogether)\nDATA=&amp;quot;[]&amp;quot;\ncurl -s -X PUT \\\n-H &amp;quot;Authorization: sso-key ${GODADDY_KEY}:${GODADDY_SECRET}&amp;quot; \\\n-H &amp;quot;Content-Type: application/json&amp;quot; \\\n-d &amp;quot;${DATA}&amp;quot; \\\n&amp;quot;https://api.godaddy.com/v1/domains/${GODADDY_DOMAIN}/records/TXT/${RECORD_NAME}&amp;quot;\necho &amp;quot;GoDaddy CLEANUP-HOOK: Removed TXT record for ${CERTBOT_DOMAIN}.&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;scheduling-with-crontab&#34;&gt;Scheduling With Crontab&lt;/h2&gt;\n&lt;p&gt;After confirming the manual hooks work, I automate the renewal checks using crontab:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /folder/to/save/cronlog.log 2&amp;gt;&amp;amp;1\n10 0 * * * /etc/letsencrypt/scripts/certbot-renew-dns.sh &amp;gt;&amp;gt; /folder/to/save/cronlog-manualhook.log 2&amp;gt;&amp;amp;1\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;pro-tips&#34;&gt;Pro Tips&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;DNS Propagation&lt;/strong&gt;: Sometimes DNS changes can take longer than expected, especially if my DNS TTL is high. I might adjust my &lt;code&gt;sleep&lt;/code&gt; time or TTL in the script if validations fail.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;: I ensure my hook scripts are &lt;strong&gt;executable&lt;/strong&gt; by using &lt;code&gt;chmod +x /path/to/script.sh&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: I keep my API keys secret. Storing them in environment variables or a secure file is best.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: I run &lt;code&gt;certbot renew --dry-run&lt;/code&gt; to verify that my hooks and scripts work before scheduling them in production.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Using Certbot’s DNS challenge manual hook scripts, I can seamlessly automate the issuance and renewal of wildcard certificates. Gone are the days of logging into my DNS provider’s dashboard every few months. With minimal scripting and a few Cron entries, my wildcard certificates can renew without me lifting a finger.&lt;/p&gt;\n&lt;!-- If this guide was helpful, [check out more of my tech insights at BluePie](https://bluepie.in) or leave a comment below to share your experience or ask questions! --&gt;\n&lt;p&gt;Leave a comment below to share your experience or ask questions!&lt;/p&gt;\n&lt;p&gt;Merry Christmas &amp;amp; A Happy New Year, See ya soon~&lt;/p&gt;</description></item><item><title>Project IDX: A Look Into Google’s Cloud-Based Development Platform</title><link>//bluepie.in/2024/11/project-idx/</link><pubDate>Sun, 24 Nov 2024 21:14:29 +0000</pubDate><guid>//bluepie.in/2024/11/project-idx/</guid><description>\n&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;\n&lt;p&gt;I’ve been experimenting with &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX&lt;/a&gt; lately, and it’s quite an intriguing solution for cloud-based development. In essence, Project IDX gives you an always-available coding environment running on Google Cloud. As long as you keep the instance running and remain active, you effectively have a &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; development machine at your disposal.&lt;/p&gt;\n&lt;p&gt;I primarily use Project IDX for Java and front-end development on my personal projects. One of the coolest parts of my setup is using it with &lt;strong&gt;Samsung DeX&lt;/strong&gt;. By simply connecting a mobile device, opening Project IDX in the browser, and plugging in a keyboard and mouse, you can achieve a desktop-like workflow from anywhere.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;my-experience-with-project-idx&#34;&gt;My Experience with Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;quick-start&#34;&gt;Quick Start&lt;/h3&gt;\n&lt;p&gt;The onboarding process is straightforward: sign in with your Google account, spin up an IDX instance, and you’re off to the races. Under the hood, Project IDX uses Nix to manage packages and dependencies, allowing you to define your environment as code.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;System specs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-htop.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;using-nix&#34;&gt;Using Nix&lt;/h3&gt;\n&lt;p&gt;Nix is a functional package manager that ensures reproducible, declarative environments. Within Project IDX, you can configure all your tooling in a &lt;code&gt;dev.nix&lt;/code&gt; file:&lt;/p&gt;\n&lt;p&gt;below is my actual config file from a project.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-nix&#34;&gt;{ pkgs, ... }: {\nchannel = &amp;quot;stable-24.05&amp;quot;;\npackages = [\npkgs.docker-compose\npkgs.zulu17\npkgs.nodejs_20\npkgs.gradle\npkgs.htop\npkgs.sudo\npkgs.pnpm\npkgs.deno\npkgs.tmux\n];\nservices.docker.enable = true;\nservices.postgres = {\nenable = true;\n};\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When your workspace initializes, Project IDX automatically installs and configures everything specified in this file. It’s fast, consistent, and eliminates the classic “it works on my machine” problem.&lt;/p&gt;\n&lt;h3 id=&#34;ai-integration&#34;&gt;AI Integration&lt;/h3&gt;\n&lt;p&gt;Project IDX is essentially a modified version of open-source VS Code, but with &lt;strong&gt;deep integration of Google’s AI&lt;/strong&gt;. You can use conversational prompts to perform tasks like creating files, refactoring code, or running commands. This isn’t just code suggestions—Project IDX can physically change files based on your instructions in the integrated chat window. It’s wild to see in action! when it actually works, it still needs work, it will only get better from here? so its okey I guess.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/project-idx-usage-onproject-chat.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Even in the above image, when instructed to create a folder similar to an existing folder, it created the folder but the latest msg from the AI shows the command failed, the editor failed to parse the command. which resulted in an error. &amp;ldquo;user-management.scss&amp;rdquo;&lt;/p&gt;\n&lt;h3 id=&#34;port-forwarding&#34;&gt;Port Forwarding&lt;/h3&gt;\n&lt;p&gt;Forwarding ports is also streamlined. When you start up a development server—say, a Java Spring Boot app on port 8080—it automatically detects the listening port and allows you to expose it publicly or keep it private. This is great if you’re running multiple services, like:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Spring Reactor app on port 8080&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;Front-end dev server on port 7173&lt;/li&gt;\n&lt;li&gt;Database on a dedicated port (e.g., 5432 or 27017)&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;A database manager like Adminer runnin inside docker and exposes port on 8181\n..etc&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;This setup makes it simple to preview projects from any device or share them with collaborations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/nov2024/open-ports-porject-idx.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;pros-and-cons-of-project-idx&#34;&gt;Pros and Cons of Project IDX&lt;/h2&gt;\n&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Cloud Accessibility&lt;/strong&gt;: No need for a powerful local machine; you can develop from virtually anywhere, even on a modest laptop or mobile device.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;AI-Driven&lt;/strong&gt;: Built-in AI assistance (sometimes referred to as “Gemini AI”) can refactor code, generate boilerplate, or provide debugging tips.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Preconfigured Templates&lt;/strong&gt;: Supports a wide range of frameworks (React, Angular, Next.js, Flutter, Python, Go, etc.). You can quickly spin up a new project with minimal setup.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Collaboration Features&lt;/strong&gt;: Real-time collaboration (shared terminals, pair debugging) is ideal for remote teams.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Nix-Based Customization&lt;/strong&gt;: Define your entire dev environment in a single file for reproducibility across projects.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;strong&gt;Performance Limits&lt;/strong&gt;: As of now, the default VM specs (currently as of writing 8GB RAM, 2 CPU cores) can feel slow when running multiple services in running parallel (docker db service, db viewer, node-frontend..etc) and heavy IDE tasks (like Java code completion).&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Beta Uncertainties&lt;/strong&gt;: Project IDX is still in beta, so things can change. There’s no official SLA, and certain features could be introduced or deprecated.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Pricing Future&lt;/strong&gt;: Currently free, but Google may charge for more powerful machines or advanced features down the road.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Dependent on Internet&lt;/strong&gt;: You need a reliable internet connection. If you lose connectivity, you lose access to your dev environment.&lt;br /&gt;&lt;/li&gt;\n&lt;li&gt;&lt;strong&gt;Growing Ecosystem&lt;/strong&gt;: While extensions exist, it’s not as mature as traditional VS Code.&lt;br /&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-takeaways&#34;&gt;Personal Takeaways&lt;/h2&gt;\n&lt;p&gt;Despite occasional slowdowns—especially with Java’s language server—I find Project IDX to be &lt;strong&gt;fast and reliable&lt;/strong&gt; for quick tests and collaborative workflows. If your instance gets messed up, you can simply reset the VM. Your user storage partition remains intact, meaning you don’t lose any data.&lt;/p&gt;\n&lt;p&gt;However, for heavier workloads or large multi-service applications, the 8GB RAM limit can become a bottleneck. If you find yourself needing more horsepower, you can request an increase from Google, but it’s not an automated process yet.&lt;/p&gt;\n&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;\n&lt;p&gt;For developers curious about cloud-based workstations, Project IDX is worth exploring. It balances the convenience of a full Linux environment in your browser with advanced AI-driven coding tools. With Nix managing your dependencies, your environment is reproducible, shareable, and straightforward to set up. While the platform isn’t perfect—still in beta, with performance and resource constraints—it’s a promising glimpse into the future of development.&lt;/p&gt;\n&lt;p&gt;If you have any questions or experiences of your own, feel free to drop a comment or reach out. Happy coding!&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;References &amp;amp; Further Reading&lt;/em&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official Project IDX Website&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://developers.google.com/idx/guides&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Developers Guide&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://nixos.org/manual/nix/stable/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Nix Official Documentation&lt;/a&gt;&lt;br /&gt;\n- &lt;a href=&#34;https://community.idx.dev/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Project IDX Community Forum&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;(Note: All information is based on personal experience and available details as of November 2024. Features and pricing may change as Project IDX evolves.)&lt;/em&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update-thoughts-on-life-edited-updated-on-jan24-2025&#34;&gt;Personal Update (Thoughts on Life (edited/updated on jan24/2025))&lt;/h2&gt;\n&lt;p&gt;Sometimes, life presents crossroads where you wonder if it’s better to step aside so someone else can be happier—even if it stings. Is that genuine sacrifice, or is it avoidance? The line can be blurry, but the hope is that such tough decisions lead to growth or peace for everyone involved.&lt;/p&gt;\n&lt;p&gt;I’m planning for a bit of rest and enjoyment in December—a week unplugged from the world’s noise (work..).&lt;/p&gt;\n&lt;p&gt;See you all soon!&lt;/p&gt;\n&lt;hr /&gt;</description></item><item><title>Rooting My Pixel For Unlimited gphotos backup</title><link>//bluepie.in/2024/04/rooting-my-pixel/</link><pubDate>Thu, 04 Apr 2024 03:14:06 +0000</pubDate><guid>//bluepie.in/2024/04/rooting-my-pixel/</guid><description>\n&lt;p&gt;All the previous android phones I had, I rooted them and used them with a custom ROM that had gphotos mod or used it with gphotos mod, I&amp;rsquo;ve had a pixel for android development as my second device..but all this time, it had stock android and not rooted with no modifications.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;But what do I mean by gphotos mod..?\nYou get unlimited photos and videos backup in Google photos, the mod makes it seem like you&amp;rsquo;re using a pixel device. But I already have a pixel device..?.. Google previously had announced that their pixel phones will have unlimited backup, but they reverted this decision after thier initial announcement.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixel-unlimited-photos-google-help.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;google-pixel-phone-storage-offers&#34;&gt;Google Pixel Phone Storage Offers&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Pixel Model&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Unlimited Storage Offer&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Original Pixel&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;No limits on photo and video storage.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 2&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 16, 2021.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Unlimited Original quality (free)&lt;/td&gt;\n&lt;td&gt;Applies only to photos and videos taken before January 31, 2022.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Pixel 3a and onwards&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Not available&lt;/td&gt;\n&lt;td&gt;Instead, offers a 3-month trial for the 100 GB Google One plan (Pixel 4, 4a, 4a (5G), Pixel 5&amp;hellip;.etc)&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h3 id=&#34;google-photos-storage-policy-changes&#34;&gt;Google Photos Storage Policy Changes&lt;/h3&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/th&gt;\n&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;November 2020&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Announced end of free unlimited storage for High quality and Express quality starting June 1, 2021&lt;/td&gt;\n&lt;td&gt;Users must use Storage saver or Original quality; new photos count towards the user&amp;rsquo;s shared storage quota.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Renamed &amp;ldquo;High quality&amp;rdquo; to &amp;ldquo;Storage saver&amp;rdquo;&lt;/td&gt;\n&lt;td&gt;Existing photos remain unaffected; new uploads consume storage quota across Google Drive, Gmail, and Photos.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;strong&gt;Post-June 1, 2021&lt;/strong&gt;&lt;/td&gt;\n&lt;td&gt;Pixel smartphone owners up to Pixel 5 remain exempt from the storage changes&lt;/td&gt;\n&lt;td&gt;Owners can continue enjoying unlimited Original quality storage despite the policy changes.&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/wikipedia-google-photos-storage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you read the support page or the wikipedia or any source, it is clear that you get original quaility backups for free for the original pixel device, which is the first pixel device, though im having a new pixel device if I emulate the first pixel device, I can get unlimited storage.&lt;/p&gt;\n&lt;h3 id=&#34;rooting-magisk&#34;&gt;Rooting &amp;amp; Magisk&lt;/h3&gt;\n&lt;p&gt;Back in 2015.. along those times. Rooting was mainly SuperSU, you install a custom recovery and flash SuperSU, or install an apk like (kingRoot..or something similar..there was a long list, I remember this just because of the name, easy to remember). The apk way to root is bascailly the app tries the known os vulnerabilities to do previlege escaltion for that specific android version. Then you would have superSU installed on the device and use the app to grant permissions to any app that uses it. Some of the apps that I&amp;rsquo;ve used are app2sd, titanium backup, (backup any app with data)..2:22am and having hard time remembering the apps that i&amp;rsquo;ve used over the years.. those were the days, have fond memories now.&lt;/p&gt;\n&lt;p&gt;Nowadays there&amp;rsquo;s just one main way.. and that is to use Magisk.&lt;/p&gt;\n&lt;p&gt;To install magisk, follow the &lt;a href=&#34;https://topjohnwu.github.io/Magisk/install.html&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;magisk official docs&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;For my device, which is a pixel, I need to get the original &lt;code&gt;boot&lt;/code&gt; img of the current firmware version I have, this we can check in About Phone or notification shade in android 13 and above(could be from before.. started noticing only it from then on). (&lt;a href=&#34;https://developers.google.com/android/images#lynx&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Official pixel images&lt;/a&gt;) Once I downloaded the zip, extracted it and found the boot img, I then followed the following instructions from official manual.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;click image to enlarge&lt;/em&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/patched_img_instructions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;bluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.039s]\nFinished. Total time: 0.039s\nbluepie@dev-machine pixel backup % adb reboot bootloader\nbluepie@dev-machine pixel backup % fastboot flashing unlock\nOKAY [ 0.006s]\nFinished. Total time: 0.006s\nbluepie@dev-machine pixel backup % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nfastboot: error: cannot load &#39;magisk_patched-27000_EyWu8.img&#39;: No such file or directory\nbluepie@dev-machine pixel backup % cd ..\nbluepie@dev-machine Desktop % cd ..\nbluepie@dev-machine ~ % fastboot flash init_boot magisk_patched-27000_EyWu8.img\nSending &#39;init_boot_b&#39; (8192 KB) OKAY [ 0.211s]\nWriting &#39;init_boot_b&#39; OKAY [ 0.014s]\nFinished. Total time: 0.228s\nbluepie@dev-machine ~ % fastboot reboot\nRebooting OKAY [ 0.000s]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once Magisk is installed and on reboot, you will see warning image that bootloader is unlocked saying device is insecure (which is true),&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;install-software-for-gphotos&#34;&gt;Install software for Gphotos&lt;/h3&gt;\n&lt;p&gt;Once Magisk is installed and phone is rooted, we need something called &lt;a href=&#34;https://github.com/rovo89/Xposed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Xposed&lt;/a&gt; (it exposes/ enables/ writes ways for us to modify certain parts of the system by exposing them as API). Back.. early days Xposed was the king..(its archived and other projects took over). One such is &lt;a href=&#34;https://github.com/LSPosed/LSPosed&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;LSPosed&lt;/a&gt; (but this only works till Android 13, again project archived). There is a fork of the lsposed which gives support for android 14. Since as of writing this.. I&amp;rsquo;m on Android 14..and this android version.. i.e. latest android 14..is not supported by lspoed because its archived by that point, so I&amp;rsquo;ll be flashing the latest lsposed &lt;a href=&#34;https://github.com/re-zero001/LSPosed-Irena&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;v1.9.2&lt;/a&gt; inside magisk. Once installed..reboot the device. Now you have lsposed installed.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/lsposed.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;pixelify-gphotos&#34;&gt;Pixelify gphotos&lt;/h3&gt;\n&lt;p&gt;The reason why we do all this because, for pixelify gphotos (to enable unlimited photos &amp;amp; video uploads), we need to install magisk and have lsposed (as of android 14). Just install the module and enable it for Google photos and reboot. Open the Pixelify gphotos app and set to spoof Pixel XL devices, thats the last step.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/april2024/pixelify-gphotos.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;&lt;/p&gt;\n&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;\n&lt;p&gt;Get unlimited backups until this is patched.. I know irl friends that use this to upload xx gbs of photos. I&amp;rdquo;ve used before, since now that I enabled this, gonna upload all my photos from the gap (3 years of not using this) to backup using this mod.&lt;/p&gt;\n&lt;p&gt;Photos uploaded through this mechanism do not take space on the account.&lt;/p&gt;\n&lt;div style=&#34;display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/photos-backed-up-for-free.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/magisk_modules.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/april2024/spoof_logs.png&#34; alt=&#34;&#34; style=&#34;height:480px;&#34;&gt;\n&lt;/div&gt;\n&lt;p&gt;Couple of other cool things with Xposed, can emulate an unrooted device with patches and make banking apps work, but I have a separate phone just for that. This pixel is just for development and photos backup (additional bonus)&lt;/p&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update&lt;/h3&gt;\n&lt;p&gt;I&amp;rsquo;ve been slipping on my dreams.. though I am happy on some parts of my life, &lt;!-- I found a special person, It happened in the new office, we were strangers first, I did not want to know them.. or wanted to have anything with them, then we became friends over video games and guitar, we became close friends over the months, then on feb 2024, we got together. Knowing we couldn&#39;t be together..I started to erase her in my life..how I did other, but she confronted me and got me into 2 days of love.. then asked for additional two days.. then additional 6 months, I asked what&#39;s after 6 months. she said.. that my birthday was in 6 months..(august 29). I gotta get back to the groove. --&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Whispers Between Strings&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In a place of new beginnings, under fluorescent glow,\nI crossed paths with a soul I thought I’d never know.\nA stranger in the crowd, I turned the other way,\nNo intentions in my heart for friendship’s gentle sway.&lt;/p&gt;\n&lt;p&gt;But fate had strings to strum, and games for us to play,\nThrough chords and pixels bright, we whiled hours away.\nFrom strangers into friends, we wove a silent thread,\nMonths unfolded softly, with every word unsaid.&lt;/p&gt;\n&lt;p&gt;Come February’s whisper, we stepped across the line,\nEntangled in a moment, pretending all was fine.\nYet shadows loomed around us, a truth we couldn’t flee,\nI tried to fade her presence, like footprints in the sea.&lt;/p&gt;\n&lt;p&gt;But she held on with courage, refused to let it end,\n“Just two more days,” she pleaded, “let hearts begin to mend.”\nThen days turned into months, six circles ‘round the sun,\n“What happens when time’s up?” I asked. She said, “We’ve just begun.”&lt;/p&gt;\n&lt;p&gt;“Your birthday marks the turning,” she smiled with knowing eyes,\nA hint of something deeper, a plan beneath disguise.\nNow time’s a path before me, with rhythm to approve,\nIt’s time to find my footing, and get back in the groove.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Echoes in the Labyrinth&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;In the maze of new beginnings, I walked alone,\nContent with solitary paths, untraveled and unknown.\nBut whispers of a melody, a song of strings and play,\nLured me toward a crossroads on an unexpected day.&lt;/p&gt;\n&lt;p&gt;A fellow traveler appeared, with laughter in her eyes,\nWe danced through hidden corridors beneath the open skies.\nWith every game and note we shared, the walls began to fade,\nThe labyrinth seemed less daunting, its edges softly frayed.&lt;/p&gt;\n&lt;p&gt;As winter turned to springtime, our footsteps fell in rhyme,\nWe found a secret clearing, a moment out of time.\nBut shadows loomed to tell us that our paths were not the same,\nI tried to turn away to where no memory spoke her name.&lt;/p&gt;\n&lt;p&gt;Yet she reached out and held my gaze, “Just stay a little more,”\nTwo days became a promise, then extended to a score.\n“What’s beyond these winding trails?” I asked beneath the moon.\nShe smiled, “A new horizon comes with August’s afternoon.”&lt;/p&gt;\n&lt;p&gt;Now the maze feels less confining, each step a subtle groove,\nEmbracing twists and turns ahead, I find my feet can move.\nFor in this intricate design, where every choice is free,\nI’m learning that the journey means as much as destiny.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;&lt;em&gt;Letter to the Universe:&lt;/em&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;Dear Universe,&lt;/p&gt;\n&lt;p&gt;I didn’t plan for this convergence of souls. In the hustle of new surroundings, I was content navigating alone, charting my course without expectation. Then, unexpectedly, someone entered my orbit—a bright comet against the night sky of routine.&lt;/p&gt;\n&lt;p&gt;We connected over shared joys: the strategic thrill of games, the harmony of strings vibrating in unison. These simple pleasures wove threads between us, creating a tapestry I hadn’t anticipated.&lt;/p&gt;\n&lt;p&gt;As time flowed, our paths aligned more closely than I thought possible. Yet, knowing the stars might pull us in different directions, I tried to drift away, to return to the solitude I once knew. But she reached out, her resolve unwavering, asking me to remain—not forever, just a little longer.&lt;/p&gt;\n&lt;p&gt;“Stay for two more days,” she said, and then, “Let’s see where six months takes us.”&lt;/p&gt;\n&lt;p&gt;“What’s at the end of that journey?” I wondered.&lt;/p&gt;\n&lt;p&gt;She hinted at a milestone—my own revolution around the sun, a birthday that could mark new beginnings.&lt;/p&gt;\n&lt;p&gt;So here I stand, Universe, open to the possibilities you lay before me. I don’t know where this path leads, but perhaps that’s the point of the journey. I’m ready to embrace the rhythm you’ve set, to find my place within the greater symphony.&lt;/p&gt;\n&lt;p&gt;Guide me as I step back into the groove of life’s unfolding melody.&lt;/p&gt;\n&lt;p&gt;With cautious hope,&lt;/p&gt;\n&lt;p&gt;A Wanderer&lt;/p&gt;\n&lt;/blockquote&gt;</description></item><item><title>My Stab at Using Ai for Productivity</title><link>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</link><pubDate>Tue, 20 Feb 2024 21:56:24 +0000</pubDate><guid>//bluepie.in/2024/02/my-stab-at-using-ai-for-productivity/</guid><description>\n&lt;h2 id=&#34;my-ai-usecases&#34;&gt;My ai usecases&lt;/h2&gt;\n&lt;h2 id=&#34;purchasing-ai-using-it-as-api-vs-monthly-subscription-for-chatgpt-claude&#34;&gt;Purchasing AI - Using It as API vs Monthly Subscription for ChatGPT/Claude&lt;/h2&gt;\n&lt;p&gt;Do you need to buy monthly subscriptions? Take Copilot and other examples. To test this out, I bought $5 worth of usage from OpenAI, used an extension for VSCode for ai  operations, and used ai tools that require api token..like AI Commits. Did it work? Yes. Have I used all of those $5? No, it&amp;rsquo;s still there. Usually, the models I use for these kinds of tasks are very affordable and don&amp;rsquo;t accumulate significant costs.&lt;/p&gt;\n&lt;p&gt;At the time of updating this post, the pricing is as follows (I&amp;rsquo;ve updated the following as of November 2, 2024):&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Model Pricing Overview&lt;/strong&gt;&lt;/p&gt;\n&lt;table&gt;\n&lt;thead&gt;\n&lt;tr&gt;\n&lt;th&gt;Model&lt;/th&gt;\n&lt;th&gt;Pricing&lt;/th&gt;\n&lt;th&gt;Pricing with Batch API*&lt;/th&gt;\n&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;gpt-4o-mini-2024-07-18&lt;/td&gt;\n&lt;td&gt;$0.150 / 1M input tokens&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M input tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.075 / 1M cached** input tokens&lt;/td&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;&lt;/td&gt;\n&lt;td&gt;$0.600 / 1M output tokens&lt;/td&gt;\n&lt;td&gt;$0.300 / 1M output tokens&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;p&gt;1M - 1Million, yea, there should be no pricing..but that pricing is low right??&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Following is the usage for a month, the usage does not exceep any high numbers&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/feb-api-usage.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to use AI, you don&amp;rsquo;t have to pay monthly. You can just buy API usage for $5 and start from there. That&amp;rsquo;s my current verdict, though it could change in the future&lt;/p&gt;\n&lt;h2 id=&#34;goal&#34;&gt;Goal&lt;/h2&gt;\n&lt;p&gt;To test whether AI increases productivity or not. I started using it and I have couple of workflows, let see one by one.&lt;/p&gt;\n&lt;h2 id=&#34;an-assistant-for-api-syntax-and-docs&#34;&gt;An Assistant for API Syntax and docs&lt;/h2&gt;\n&lt;p&gt;An assistant that just gives the command out for common usecases, these are stuff that I nornally would google and take some tiem doing the same, here I jsut ahve a static window open with custom instruction, if you have used chatgpt or claude or any other ai chatbots. it yaps a lot, or give unnecessary explanations.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/chat-apt-api-window.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ll open this floating sticky window to my desktop and I&amp;rsquo;ll start coding/ imeplenting what I needed to do, when doubts, I just type in this window ,I get answers. I wont ask anything I wont&amp;rsquo; know, I just ask stuff which i already know but dont remember. I wouldn&amp;rsquo;t recommend using output from this directly without research.&lt;/p&gt;\n&lt;h2 id=&#34;zed&#34;&gt;Zed&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://zed.dev&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;zed&lt;/a&gt;, I have configured it with open AI key and I can use these features with the help of this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;inline-ai&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/zed-inline-ai.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;chat window&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/ai-chat-window-with-different-features.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;model selection&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/model-selection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;git-helper&#34;&gt;Git helper&lt;/h2&gt;\n&lt;p&gt;I&amp;rsquo;ve been using ai commits, configuring in terminal.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/aicommits.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve stopped using ai commits, cuz a chatgpt window with custom instructions in much btter than this.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/git_commit_message_helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Again if you notice, chatgpt doesnot spew any additional information, its fed with instructions as should only give git commit messages. I just copy pasted the rules from this page &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;conventional commits.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;Again all these are open source projects of mine, so im pasting my git diff into it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# im using mac, for copying to clipboard pbcopy\ngit diff --staged | pbcopy\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;advanced-find-in-page-helper&#34;&gt;Advanced find in page helper&lt;/h2&gt;\n&lt;p&gt;This is powerful&amp;hellip;maybe its bad longterm? idk.. before I have to learn entire docs to find a specific information if I can&amp;rsquo;t find it with keywords with find in page. &lt;em&gt;NOW WE CAN SEARCH THE CONTENT WITH LOGIC, WITHOUT EXACT WORDS&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;what to do achive this? First select all content of the page and give it instructions to specifically find a info using logic, tell it to use information only provided in the content.&lt;/p&gt;\n&lt;p&gt;See the example below, it greatly reduces my time for implementing and learning items.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/advanced_find_in_page.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;phrases-english-helper&#34;&gt;Phrases - English - Helper&lt;/h2&gt;\n&lt;p&gt;Self explanatory, I hear a lot of phrases from tv shows, utube, needed a explainer with custom instructions of using that in 2 sentences.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/feb2024/phraes-english-helper.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;There should be a continuation..to the post. obviously when I use more and more..I&amp;rsquo;ll find more stuff.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;You can create ai assistant based on your needs to reduce your time working on things and increase your productivity.. these are the usecases I find for now.. should find more ones in future.&lt;/p&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal update&lt;/h2&gt;\n&lt;p&gt;Found another frost, getting a lot of life experiences.. I know it will not work between surroundings and struggles for future, deciding on august 29th, hopefully they understand.. still want entire life. Suggestion from frost 2 is just live in the moment and enjoy?. Future me.. tell me how it is going?.. remember feb 2024?. Good luck, stay positive and have a little bit of hope!.&lt;/p&gt;</description></item><item><title>Creating and Publishing Chrome Extension in Chrome Store</title><link>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</link><pubDate>Wed, 03 Jan 2024 14:31:56 +0000</pubDate><guid>//bluepie.in/2024/01/creating-and-publishing-chrome-extension-in-chrome-store/</guid><description>\n&lt;h2 id=&#34;why-motivation&#34;&gt;Why &amp;amp; Motivation&lt;/h2&gt;\n&lt;p&gt;Wanted an adblocker for accuradio website, I just need to silence the ads on this website. How can it be done? Custom script that get injected into the page by some extensions such as tamper monkey, the disadvantage is u gotta install tamper monkey. The other idea I have in my brain is I Wanted to try out chrome extesnion that specifically does this job, since my current work needs me work on chrome extensions.&lt;/p&gt;\n&lt;p&gt;Wanted to do for a while, the ads on accuradio is annoying, I wanted to write a script to block it, i wanted to just install it and be done with it. Even if I use a new machine/laptop.. i dont have to set it up or anything, just install the extension and use the website.&lt;/p&gt;\n&lt;h2 id=&#34;how-chrome-extensions-work&#34;&gt;How chrome extensions work&lt;/h2&gt;\n&lt;p&gt;How to publish chrome extenision, how chrome extensions work? why they are necessary?&lt;/p&gt;\n&lt;p&gt;Chrome extensions are essentially web apps that use web technologies but are limited to the Chrome browser. They run in a sandboxed environment, ensuring they don&amp;rsquo;t interfere with the webpage content unless explicitly designed to do so.&lt;/p&gt;\n&lt;p&gt;Background Scripts: Handle the behind-the-scenes logic, such as listening for specific browser events.&lt;/p&gt;\n&lt;p&gt;Content Scripts: Injected into web pages to modify their content or behavior.\nPopup HTML: Defines the UI, which appears when users click the extension icon.\nExtensions: communicates with web pages through messages and shared storage, maintaining separation while enabling interaction.&lt;/p&gt;\n&lt;h2 id=&#34;to-publish-new-extensions&#34;&gt;To publish new extensions&lt;/h2&gt;\n&lt;p&gt;To publish a Chrome extension, we must create a developer account on the Google Chrome Web Store. As of writing this post, Google requires a one-time registration fee of 5 USD for setting up this account. This fee is intended to verify the developer&amp;rsquo;s identity and to discourage spam and fraudulent extensions on the platform.&lt;/p&gt;\n&lt;h3 id=&#34;extension-code&#34;&gt;Extension code&lt;/h3&gt;\n&lt;p&gt;Extension code that is responsible to detect the ads on the page.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt; observer = new MutationObserver((mutations) =&amp;gt; {\nmutations.forEach((mutation) =&amp;gt; {\n// filter the target to contain only if it is adPlaying or not\nif (\nmutation &amp;amp;&amp;amp;\nmutation.target &amp;amp;&amp;amp;\nmutation.target.cloneNode().classList &amp;amp;&amp;amp;\nmutation.target.classList.contains(&amp;quot;adPlaying&amp;quot;)\n) {\n// mute the tab\n} else {\nif (chrome &amp;amp;&amp;amp; chrome.runtime) {\n// unmute the tab\n}\n}\n});\n});\nobserver.observe(accuradioPlayerControls, {\nattributes: true,\n});\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;When creating Chrome extensions, it&amp;rsquo;s crucial to handle permissions carefully. Given that Chrome is designed with a robust focus on security and user privacy, any extension listed on the Chrome Web Store is required to request only the permissions essential for its functions. The principle of &amp;ldquo;minimum necessary privileges&amp;rdquo; is not just a best practice in security; it is a fundamental requirement. This principle dictates that an extension should operate with the least amount of privileges it needs to function properly.&lt;/p&gt;\n&lt;p&gt;I had this happen to me, more permission than I actaully needed, so my first submission was rejected, but then I re-submitted with the permissions fine grained, it got approved.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/jan2024/chrome-store-rejection.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;link-to-chrome-extension&#34;&gt;Link to chrome extension:&lt;/h3&gt;\n&lt;p&gt;&lt;a href=&#34;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://chromewebstore.google.com/detail/mute-audio-ads-on-website/lhgmlkkpdhcgenmglmbomhemkkgccjef&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h3 id=&#34;personal-update&#34;&gt;Personal update:&lt;/h3&gt;\n&lt;p&gt;Brought my mom to chennai; im getting home food again. close friend left to hometown. Mom asking me to buy stuff for the new home; so doing that. Going pretty well~&lt;/p&gt;</description></item><item><title>How I Set Up Nginx and SSL for My Media Server and Torrent</title><link>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</link><pubDate>Thu, 07 Dec 2023 20:49:16 +0000</pubDate><guid>//bluepie.in/2023/12/how-i-configure-nginx-for-my-server-and-torrent/</guid><description>\n&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;\n&lt;p&gt;This post is a nginx configurations of my &lt;a href=&#34;https://bluepie.in/2023/11/self-hosted-netflix-replacer/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;How I replaced Netflix and started using my media server for over 2 years now&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;config of jellyfin&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;We already run jellyfin inside podman and expose the service on port 8096, so we use nginx to handle requests to domain &amp;lsquo;media.bluepie.in&amp;rsquo;, which connects to jellyfin podman instance.&lt;/p&gt;\n&lt;p&gt;I point my domain (through domain provider) to the ip address of the server im running my jellyfin instance, then use nginx to serve the requests, the nginx configs are located inside config folders and are included in main file.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Setting Up Nginx (Reminder for me, others ignore)&lt;/strong&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Install Nginx.&lt;/li&gt;\n&lt;li&gt;Then, make a config file in the conf.d folder. I use one config file for each domain and add it to the main Nginx config with /etc/nginx/conf.d/*.conf&lt;/li&gt;\n&lt;li&gt;Check Nginx config: &lt;code&gt;nginx -t&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/li&gt;\n&lt;li&gt;Reload Nginx: &lt;code&gt;sudo service nginx reload&lt;/code&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config file for media server&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server {\nserver_name media.bluepie.in;\nlocation / {\nproxy_pass http://127.0.0.1:8096;\nproxy_pass_request_headers on;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Protocol $scheme;\nproxy_set_header X-Forwarded-Host $http_host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $http_connection;\nproxy_buffering on;\nproxy_redirect / / ;\nproxy_cookie_path / /;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;config of torrent&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Instead of maintaining a separate domain for running qBittorrent, I utilize the main domain but with the &amp;lsquo;/torrent/&amp;rsquo; prefix. To enhance security and prevent others from accessing it, I implement two layers of protection: HTTP basic authentication. For this purpose, a new .htpasswd file is created and linked with Nginx for handling HTTP basic login before accessing qBittorrent. After the initial login to qBittorrent, the username and password are configured. Subsequently, during subsequent logins, users will be prompted for HTTP basic authentication first and then for qBittorrent authentication.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Creating a .htpasswd File for Authentication:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;(self note: The auth_basic and auth_basic_user_file directives are built-in and don&amp;rsquo;t require any extra modules)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Creation:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;htpasswd -c /etc/nginx/.htpasswd &amp;lt;username&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Subsequently, specify its usage in Nginx:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;auth_basic &amp;quot;Restricted Access&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;Config to be added to main nginx config&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; location /torrent/ {\nproxy_pass http://127.0.0.1:8181/;\nproxy_set_header Host $host;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection upgrade;\nproxy_set_header Accept-Encoding gzip;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Port $server_port;\nauth_basic &amp;quot;restricted space&amp;quot;;\nauth_basic_user_file /etc/nginx/.htpasswd;\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;!-- - ssl config for all sites, including a bounds dns, tell that I should automate based on godaddy api. --&gt;\n&lt;p&gt;I automate SSL certificate renewals across my devices with Certbot, ensuring secure, uninterrupted service without manual oversight. The heart of this automation is a daily cron job configured to run Certbot&amp;rsquo;s renewal command, specifically opting for the &amp;ndash;preferred-chain &amp;ldquo;ISRG Root X1&amp;rdquo; to ensure broad compatibility (particularly for setting up custom DNS servers on Android devices that are operating on version 10 or higher). Here&amp;rsquo;s the crux of the setup:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;SHELL=/bin/bash\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\n10 0 * * * /usr/bin/certbot renew --preferred-chain &amp;quot;ISRG Root X1&amp;quot; &amp;gt;&amp;gt; /home/&amp;lt;user&amp;gt;/cronlog.log 2&amp;gt;&amp;amp;1\n# The schedule 10 0 * * * means the task will run at 12:10 AM every day server time.\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This approach leverages the reliability of cron for scheduling and Certbot for execution, automating the renewal process and logging outcomes for easy monitoring. It&amp;rsquo;s a streamlined, efficient method.&lt;/p&gt;\n&lt;h3 id=&#34;an-insight-into-dns-renewal-challenges-unrelated-to-jellyfin-or-qbittorrent&#34;&gt;An Insight into DNS Renewal Challenges (Unrelated to Jellyfin or qBittorrent)&lt;/h3&gt;\n&lt;!-- usually a tmux shell is running tmux ls\n- login to server\n- check cronlog.log in home directory\n-I wrote for myself, found inside adguard\ncertbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\n- certificate for dns.bluepie.in, media.bluepie.in,uat.bluepie.in will automatically renew.\n- have to manually renew the certificate for *.dns.bluepie.in\n- certificates are stored in /etc/letsencrypt/live/dns.bluepie.in-0001\n- copy the pri.pem &amp; fullchain.pem to /home/opc/adguard/conf\n::raw_log_start::\n[root@instance-20220718-0253 dns.bluepie.in-0001]# certbot certonly --manual --preferred-challenges=dns --preferred-chain=&#34;ISRG Root X1&#34;\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\nPlease enter the domain name(s) you would like on your certificate (comma and/or\nspace separated) (Enter &#39;c&#39; to cancel): *.dns.bluepie.in\nRenewing an existing certificate for *.dns.bluepie.in\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name:\n_acme-challenge.dns.bluepie.in.\nwith the following value:\nfWQdh1jwhPtKe8Wz1NMaX2dhNt_a2Rd4iM_pYZjTIHo\nBefore continuing, verify the TXT record has been deployed. Depending on the DNS\nprovider, this may take some time, from a few seconds to multiple minutes. You can\ncheck if it has finished deploying with aid of online tools, such as the Google\nAdmin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in.\nLook for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the\nvalue(s) you&#39;ve just added. --&gt;\n&lt;p&gt;Every time I want to renews the whildcard DNS domain (i.e) *.dns.bluepie.in, I start with logging in and checking the cronlog.log in my logs Directory. This log helps me keep track of the automated renewals for all services running in that server.&lt;/p&gt;\n&lt;p&gt;This setup works well for most of my domains, automatically renewing their certificates. However, there&amp;rsquo;s a catch with the wildcard domain *.dns.bluepie.in which requires a manual renewal process every two months. After renewing, I manually move the private key and pem files do dns service so that it can read and update the certificates.&lt;/p&gt;\n&lt;p&gt;The manual part involves adding a DNS TXT record for _acme-challenge.dns.bluepie.in. and waiting for it to be verified. This process, although straightforward, is a bit of a hassle because it can&amp;rsquo;t be automated with my current setup. After updating the DNS record, I always double-check to make sure it&amp;rsquo;s correctly deployed using the Google Admin Toolbox: &lt;a href=&#34;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns.bluepie.in&lt;/a&gt;. This tool confirms the record is live, which is crucial before proceeding with the certification.&lt;/p&gt;\n&lt;p&gt;The Google Admin Toolbox, specifically the &amp;ldquo;Dig&amp;rdquo; tool, is a web-based version of the Unix dig command, which I often use for DNS verification. This tool is part of a suite designed to troubleshoot Google Workspace services and is particularly handy for checking DNS record propagation, such as the TXT records required for SSL certificate validation. I use this tool to confirm that the record is correctly propagated across the DNS system. This step is crucial because the validation of SSL certificates by Let&amp;rsquo;s Encrypt (or any other certificate authority that supports DNS-01 challenge). For more detailed functionalities and tools, you can explore the toolbox directly at &lt;a href=&#34;https://toolbox.googleapps.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;toolbox.googleapps.com​&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve been thinking about solving this issue by integrating with the GoDaddy API for DNS wildcard renewal. Doing this would remove the need for manual intervention every two months, making the process smoother and fully automated. It&amp;rsquo;s definitely something I plan to work on in the future to streamline my server management tasks.&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;personal-update&#34;&gt;Personal Update&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This section typically opens each of my posts, a tradition spanning many years. However, I&amp;rsquo;ve decided it might be better placed at the end moving forward.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve begun to engage heavily with LeetCode this year, a detail I haven&amp;rsquo;t shared in my posts. So, in case future me forgets, I&amp;rsquo;m jotting it down here for remembrance. Remember &lt;a href=&#34;https://adventofcode.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AOC&lt;/a&gt;, which I previously tackled in 2020 and maintained a GitHub repo &lt;a href=&#34;https://bluepie.in/2020/12/adventofcode-2020/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;? I&amp;rsquo;ve opted to take it on this year as well. Yes, even though my plate is overflowing, I&amp;rsquo;ve decided to add this to my queue&amp;hellip; I know, :facepalm:.&lt;/p&gt;\n&lt;p&gt;I&amp;rsquo;ve leased a new place in Chennai (Mugalivakkam), roughly 6 kilometers from my current office. It&amp;rsquo;s a 2BHK flat with parking facilities. I moved in, stating that it would be a family residence, with plans for my parents to join me. This decision came after a series of abrupt changes. Initially, I had a different plan in mind, but it&amp;rsquo;s funny how things evolve up to the last moment. My friend from the police force, with whom I&amp;rsquo;ve lived since after COVID and who recently got posted in Chennai after passing his exams, is a close friend. So, despite only needing to be in the Chennai office two days every two weeks (totalling four days a month) and despite the office policy changing around August/September to require my presence two days every week, I opted for a permanent stay over the convenience of crashing at a friend&amp;rsquo;s place. My decision was influenced by several factors:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;My close friend, who I&amp;rsquo;ve known for eight years (time really flies), got a posting in Erode and plans to move out in December 2024. He had his transfer announced just before Diwali in 2023.&lt;/li&gt;\n&lt;li&gt;Parents take care of me, homemade food, household chores, them being atpeace without worring seeing me everyday.&lt;/li&gt;\n&lt;li&gt;The home-like setup for productivity and peace of mind.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;m writing this update after December, so for me, December is already in the past. I&amp;rsquo;ve been sick and bedridden over Christmas, alone since my friends and family were in their hometowns, including my parents. Despite my mother&amp;rsquo;s insistence on coming over as soon as possible because of my neglect towards self-care and healthy eating, I plan to bring them here in January after Pongal 2024. This is another reason I chose an individual house over sharing a space with friends, opting for a PG, or having a flatmate.&lt;/p&gt;</description></item><item><title>How I Replaced Netflix and Have Been Using My Media Server for Over 2 Years</title><link>//bluepie.in/2023/11/self-hosted-netflix-replacer/</link><pubDate>Sun, 19 Nov 2023 21:55:10 +0000</pubDate><guid>//bluepie.in/2023/11/self-hosted-netflix-replacer/</guid><description>\n&lt;!-- There are telegram groups - prabh story buy things from sotre 50 rupees per month. --&gt;\n&lt;h2 id=&#34;feasibility&#34;&gt;Feasibility&lt;/h2&gt;\n&lt;p&gt;Can I host Netflix on my own? of course yes, many open source software offers similar functionality, you can set it to work only inside your home network or make it available throughout the internet.&lt;/p&gt;\n&lt;p&gt;I already own a server, I rent out a server for hosting my development activities, so why not use that server as a media server, I don&amp;rsquo;t want it to run just inside the home LAN, I want it to be exposed to the internet, of course with required security measures.. if its open others can access it.&lt;/p&gt;\n&lt;h2 id=&#34;what-i-need&#34;&gt;What I need&lt;/h2&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;p&gt;Media server, there are many to choose from using a quick Google search, I&amp;rsquo;ve chosen &lt;a href=&#34;https://github.com/jellyfin/jellyfin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Jellyfin&lt;/a&gt;, as it has all the features I need.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Application to download, media to the server, i.e. torrent client. &lt;a href=&#34;https://github.com/qbittorrent/qBittorrent&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Qbittorrent-nox&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;That&amp;rsquo;s all you ask&amp;hellip;? Yeah, I was surprised too. anyway I know I need these two software, but how am I going to install and run them on my server?&lt;/p&gt;\n&lt;!-- 12:06 AM12:06 AM --&gt;\n&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;\n&lt;p&gt;The choice here would be to use docker/podman because it won&amp;rsquo;t affect the host machine. First, to run the media server, I need to mount a new drive to store all the movies and TV shows, I chose a 150 GB drive, cuz I don&amp;rsquo;t need more than it and I can remove any torrents and add another one whenever needed.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;show disk mount volumes&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;df -h\nFilesystem Size Used Avail Use% Mounted on\n...\n/dev/sdb 150G 134G 16G 90% /mnt/external-disk\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;!-- - Jellyfin UI\n- hosting commands with Podman, mounting volumes --&gt;\n&lt;p&gt;I use Podman, this is the command I use to install Jellyfin. If you notice, I mount all the configs, cache, and media to the host.&lt;/p&gt;\n&lt;!-- uid=1000, gid=1000 --&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;podman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;wait.. is that it? Yea, it is that simple!&lt;/p&gt;\n&lt;h2 id=&#34;how-do-i-download-stuff-so-i-can-watch-it-in-jellyfin&#34;&gt;How do I download stuff, so I can watch it in Jellyfin?&lt;/h2&gt;\n&lt;p&gt;By running Qbitorrent nox, make sure you set the username and password, when you visit the web-ui, you need to be authenticated to access it.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;yum install qbittorrent-nox\nqbittorrent-nox -d --webui-port=8181\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;Qbittorrent UI screenshot&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/qbittorrentnox-search-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;access-the-web-ui-and-start-watching&#34;&gt;Access the web-ui and start watching.&lt;/h2&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/media-server-webclient.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;watch-it-on-mobile-ipad-tv-etc&#34;&gt;Watch it on mobile/ iPad/ TV..etc&lt;/h2&gt;\n&lt;p&gt;There is an &lt;a href=&#34;https://jellyfin.org/downloads/clients&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;official client for everything.&lt;/a&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;using web vs native app.. download links..etc, you need to configure the endpoint Url of Jellyfin, (covering that in a different post)&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;configuring the mobile app.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/jellyfin-mobile-configuration.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/self-hosted-netflix-replacer/mobile-playback-screenshots.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;You just have to spend a couple of hours every year in total to maintain update &amp;amp; maintain. The server costs you can share it with friends. I think people are spoon-fed nowadays~&lt;/p&gt;\n&lt;p&gt;You have to consider the fact that, the streaming wars are real, &lt;a href=&#34;https://www.youtube.com/watch?v=yvhv7bgmz64&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this video summates my frustration.&lt;/a&gt;&lt;/p&gt;\n&lt;hr /&gt;\n&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;\n&lt;p&gt;To update Jellyfin and get the latest build and not lose data, since we are running it in podman, it&amp;rsquo;s pretty easy. Check the command from top to bottom, it is self-explanatory,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt; podman pull docker.io/linuxserver/jellyfin:latest\npodman image ls -a\npodman ps\npodman stop jellyfin\npodman container ls -a\npodman container rename jellyfin jellyfin_bkp_&amp;lt;date&amp;gt;\npodman container ls -a\npodman run -d \\\n--name=jellyfin \\\n-e TZ=IN \\\n-e PUID=&amp;lt;UID&amp;gt; \\\n-e PGID=&amp;lt;GID&amp;gt; \\\n-p 8096:8096 \\\n-v /home/opc/jellyfin/cache:/cache \\\n-v /home/opc/jellyfin/config:/config \\\n-v /mnt/external-disk/torrents:/media \\\n--restart unless-stopped \\\ndocker.io/linuxserver/jellyfin:latest\npodman ps\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;personal-update-rant-about-past-backstory&#34;&gt;Personal update - Rant about past - Backstory&lt;/h2&gt;\n&lt;p&gt;&lt;em&gt;This is usually at the start of each post, but I thought it would be better suited at the bottom from now on.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Back before I got a job, basically a broke ass student, I/ we/ everyone used to pirate entertainment content all the time, u name it, movies, tv shows, music..etc, those times were before the JIO era, back when 59kbps was the unlimited internet provided by BSNL for everyone, youtube videos where limited to 240/ 360p, Aor downloading a show or game, we would run it for days, I vividly remember my friend downloading a show with 600+ episodes show for multiple days, now we got unlimited 5g, 200 Mbps unlimited broadband.. boy times have changed. I have to note it down somewhere..make sure future me remembers.&lt;/p&gt;\n&lt;p&gt;After I got my job and the JIO internet era, buying subscriptions became a common thing&amp;hellip; everyone was on the bandwagon&amp;hellip; I came in late but I did try the subscription things before I went with this approach, I knew there were alternatives and I could still pirate stuff. But if I paid the subscription, it is accessible with a click of a button. While writing this I am reminded of something..that it has made us lazy.&lt;/p&gt;</description></item><item><title>Overcoming a limitation in Electron</title><link>//bluepie.in/2023/09/electron-views/</link><pubDate>Thu, 21 Sep 2023 06:03:26 +0000</pubDate><guid>//bluepie.in/2023/09/electron-views/</guid><description>\n&lt;p&gt;Hey, what&amp;rsquo;s going on with you, hopefully good things, a small personal update, I think I lost focus on my goals for almost a 1.6 years, right after I joined vz, maybe its beause this is the first ime I ever switched my official day time job, wtf, time..time..time it moves soo fast, nowadays literally everthing takes soo much time in this attention seeking nightmare.. wtf, you can not even ignore most of the things, otherwise you will be an outcast, Something happened and it gave me a wakeup call on why I wanted my life goals on the first place, then I realized, &amp;ldquo;you are the person shaped by your memories, at least your behaviour should reflect it&amp;rdquo;, because If I still had my old memories and experiences I would have not changed courses? doing all the things that external factors influences me, my future self can agree or disagree on this.&lt;/p&gt;\n&lt;h2 id=&#34;electron&#34;&gt;Electron&lt;/h2&gt;\n&lt;p&gt;I haven&amp;rsquo;t worked in electron before, embedded view and browser views are three different ways to build the UI for your electron app&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;iframe tag&lt;/li&gt;\n&lt;li&gt;webview tag&lt;/li&gt;\n&lt;li&gt;BrowserView&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;Officailly in the docs there is note asking people to not use webiew though, &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/webview-tag#warning&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;Important Note: we do not recommend you to use WebViews&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;what-why&#34;&gt;What &amp;amp; why&lt;/h2&gt;\n&lt;p&gt;Recently I&amp;rsquo;ve been tasked with creating an electron experience on my office job, sadly I can not talk about it though, but I can share how I solved some of the engineering problems though.&lt;/p&gt;\n&lt;h2 id=&#34;this-post-is-gona-be-detailing-about&#34;&gt;This post is gona be detailing about,&lt;/h2&gt;\n&lt;p&gt;Problem to solve:\nOvercoming the lack of support of z-Index on Electron Views&lt;/p&gt;\n&lt;h2 id=&#34;figma-design-views&#34;&gt;Figma design views&lt;/h2&gt;\n&lt;p&gt;Maybe this can be solved in a different way, maybe there is even an official solution, but when I searched for zindex for browser views, there was not any solution.&lt;/p&gt;\n&lt;p&gt;For the project, I needed to work with showing different views simultaneously at the same time.&lt;/p&gt;\n&lt;p&gt;All the views are maintained and accessbile in electron api&amp;rsquo;s, In addition to that, I built a wrapper Class that track all the windows in the app, It also trackts what is the current active view.&lt;/p&gt;\n&lt;h3 id=&#34;what-i-mean-by-main-view-current-active-view&#34;&gt;What I mean by main view/ current active view?&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view-expl-part-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Section labelled as &amp;ldquo;1&amp;rdquo; is like a sidebar to a website, its main purpose of it is to switch between showing content on section labelled as &amp;ldquo;2&amp;rdquo;.&lt;/li&gt;\n&lt;li&gt;Both &amp;ldquo;1&amp;rdquo; &amp;amp; &amp;ldquo;2&amp;rdquo; are &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;BrowserViews&lt;/a&gt; positioned absolutely using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-view#viewsetboundsbounds-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;set bounds&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;\n&lt;p&gt;If the view for the left sidebar (section &amp;ldquo;1&amp;rdquo; from image) has to show a popup, that is beyound the bounds (width and height), the content in the view will be cut-off by the main view (section &amp;ldquo;2&amp;rdquo; of the image). In browser if we were to do this, we can set &lt;code&gt;z-index&lt;/code&gt; to overcome the issue, this is true even for electron, if we have used the &lt;code&gt;iframe&lt;/code&gt; instead of &lt;code&gt;BrowserView&lt;/code&gt;,&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;solution&#34;&gt;Solution&lt;/h3&gt;\n&lt;p&gt;Since &lt;code&gt;BrowserViews&lt;/code&gt; does not have &lt;code&gt;z-index&lt;/code&gt;, I have to find a different solution. In &lt;code&gt;BrowserView&lt;/code&gt; the last added window is always placed on top of other windows. So this is the reason, the sidebar view (section 1) has its view (view that is out of bounds) cut-off. But luckly we have this &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/browser-window#winsettopbrowserviewbrowserview-experimental&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;api (setTopBrowserView)&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;[API definition] - win.setTopBrowserView(browserView)&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;&lt;em&gt;Raises browserView above other BrowserViews attached to win. Throws an error if browserView is not attached to win.&lt;/em&gt;&lt;/p&gt;\n&lt;p&gt;Using &lt;code&gt;setTopBrowserView&lt;/code&gt; I can make the sidebar view to come front, wheneven I want to show a popup or any view that is out of bounds (width and height exceeds the bounds in the view).&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this&#34;&gt;How do we do this?&lt;/h3&gt;\n&lt;p&gt;For my usecase, I wanted to show a popup that will be out of bounds when user interacts with the contents of the sidebar, so I have a trigger which I can use when to show or hide out of bounds contents.&lt;/p&gt;\n&lt;p&gt;I can listen for mouse events on the side bar view (section &lt;code&gt;1&lt;/code&gt; from the image), whenever the mouse enters the sidebar view I can use &lt;code&gt;setTopBrowserView&lt;/code&gt; to set sidebarview as the &lt;code&gt;top&lt;/code&gt; view.&lt;/p&gt;\n&lt;p&gt;Now all the contents. Normal and out of bounds contents are visible, as it is the top view.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/Solving an electron problem/view expl - part 2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;On mouse leave, I can then set the original view that was top view to reset the behaviour.&lt;/p&gt;\n&lt;h3 id=&#34;how-do-we-do-this-in-react&#34;&gt;How do we do this in react?&lt;/h3&gt;\n&lt;p&gt;Code is pretty straight forward and will make it easy to understand the logic.&lt;/p&gt;\n&lt;p&gt;render part&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;&amp;lt;div\n...\nref={divListenerRef}\n&amp;gt;\n...\n...\n...\n&amp;lt;/div&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;setting up the mouse enter and mouse leave triggers&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;useEffect(() =&amp;gt; {\nif (divListenerRef &amp;amp;&amp;amp; divListenerRef.current) {\nconsole.debug(&#39;[setting bounds triggers]&#39;);\ndivListenerRef.current.addEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.addEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\nreturn () =&amp;gt; {\nif (divListenerRef.current) {\nconsole.debug(&#39;[cleaning up triggers]&#39;);\ndivListenerRef.current.removeEventListener(&#39;mouseenter&#39;, callbackToExpand);\ndivListenerRef.current.removeEventListener(&#39;mouseleave&#39;, callbackToRestrict);\n}\n};\n}, [popupCoordinates, popupProfile]);\n#popupCoordinates, popupProfile are just x,y values &amp;amp; status, whether to show or hide the popup\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;like I said code is self explanatory.\nI&amp;rsquo;ve had this in draft for over couple of months now.. I wanted to check office laptop to refer my code to be accurate, finally had the urge to open office laptop..at 408am.\nlike I said in the begining personal udpate.&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Electron is fun, using exisiting tech that we already know to speed up the dev process to deliver quick.. maybe I will cover bundling/building in a diff post..if I feel that I can contribute something different than what is already present all over the internet.&lt;/p&gt;\n&lt;p&gt;I made the bad desicision of looking back into my past.. I shouldn&amp;rsquo;t have.. and tried to make contact.. why did I do that? (not influenced by core belief&amp;hellip;suggestions?) by the time I got a response, I was already regretting and back to my current self of just moving forward. all happended in july (remember future me!)&lt;/p&gt;\n&lt;p&gt;The thing about memories,it has proven me once again. Since I started to write..complete and had this post in draft for months, I already forgot a lot.. if I remembered all the things from the begining paragraph while making decisions over the last months, I would have made differnet decisions. Memory is really important to _ your _ &amp;amp; _.. (future me remebers?) maybe I can build something for me that keeps track of all these.. with a search user interface and a keyword.. so I can make an informed decision?? google tasks, keep already do this.. no?..something to think about for me. Hope future me reads this and realises how important some small things can be.&lt;/p&gt;</description></item><item><title>Birthday Month 2023, Reflection &amp; Goals</title><link>//bluepie.in/2023/08/birthday-month-reflection/</link><pubDate>Mon, 21 Aug 2023 02:06:53 +0000</pubDate><guid>//bluepie.in/2023/08/birthday-month-reflection/</guid><description>\n&lt;p&gt;This is personal post, not about tech.. if anyone other than me is reading this.. ignore this if you wanted somehting technical.&lt;/p&gt;\n&lt;p&gt;I can not belive, its been already 3 years &lt;a href=&#34;https://bluepie.in/2020/08/future/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;since this post&lt;/a&gt;. Covid really took 2 years from my life real quick. I dont know If I&amp;rsquo;d be in a better/ lesser position if it were not for covid though.&lt;/p&gt;\n&lt;p&gt;I started this draft on august 21 and as i am inching each day towards my birthday and beyond. okey so lets compare the posts from previous years, my predictions and what not.&lt;/p&gt;\n&lt;p&gt;I did a purchase with a huge sum of money, atleast my own money without the help/assistance of my parents, so it felt pretty good. Sometimes I think maybe its a bad idea? cuz some of the suggestions when I told others were invest it somewhere else and buy it through other means. Like I always say only future holds the truth.&lt;/p&gt;\n&lt;h2 id=&#34;reflection&#34;&gt;Reflection&lt;/h2&gt;\n&lt;blockquote&gt;\n&lt;p&gt;For my future self&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Idk I feel like my judgement is clouded and I got distracted from my life/long term goal of comitting to open source.. or wait till you hear this delusionsal reasoning.. of maybe..maybe I might have leveled up and found something else? maybe.. Im doing a lot of travel back and forth.. and now since I&amp;rsquo;be been living with a close friend of mine in the work location, I had to socialize more.. which is not a bad thing.. I can not just sitback.. thinking and typing away on my keyboard.. while they were in the same/next room&amp;hellip;&lt;/p&gt;\n&lt;p&gt;Its like a limbo [removed&amp;hellip;]&lt;/p&gt;\n&lt;p&gt;Encrypting below and not removing, if future me forgets for some reason.&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+Z+HeoqIwG5YyizCq6IyINO1be3KxOWWzx6y1rvURg/YAzbhy0SxxQ\nHW3q9sdbRWrNTE57PJBcZFwVtT7NuGjLMPjiSxF1KnaTZ3JULiKbO+riM+TkkxfI\nZ0Yh2BKpSF/HRgdjI787sWXiShmJksyM+itfyO6PkcuI5Qiglq7on+ZzkOqbGID7\nGj34igwMbpFQOxUzgtC9H6dIeUzTXjxlqPExZXaGtGxVBlQIHtAekFs8igqG3yn3\nOWNHmfbtBHcsA57i4CjOLnJ2VabrLkFX/oym0+6E8aQNiYYZtVf0dlRaAhTcE/vm&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX1+fnI5zDIHyEW0/HpvcfBw1dj7tPcFafQv8HXxlxffZgoluZx9tG7jP\nDZpgSrGa4Uq9CJbCvlin+yLMt7b7SFKRJt7xNkDXO0efAkZnXy/xPWWWA2ZXeUGq\nDN5YzMv6rLtXMISiFzNZHUMiq04mj0qyHAyxZQOLjwA=&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&amp;ldquo;U2FsdGVkX19MbTbNNHu3p/uw0g+n4qUWxUhn9cbTLU75sG5oayaiiEoJ86S5O9NQ\nzRlF8ODh/y/Z/V0NF/6CTZyAyZKKw91ZtDt9lnCBaMijPUoCL2ShOmSm/ewRP8Q3\nGjrb5A6Ml8xvhhuomeH37njHWg3112JR+zhJzJ1A+nXB8AJ9uyBhew+F0FYtNDu4\nAhPfDk1Kxs18nKkIs5cOZPptQ8p4fM8fOrvlxZOMdVwHOpCi7pnrHxf0aaBTFoy8\n1skzdWQ5C7k18lPAZig1cg==&amp;rdquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n#enc\necho -n &amp;quot;string-to-encrypt&amp;quot; | openssl enc -aes-256-cbc -a -pbkdf2\n#dec\necho -n &amp;quot;string-to-decrypt&amp;quot; | base64 -d | openssl enc -d -aes-256-cbc -A -pbkdf2\n#incorrect password will print random symbols\n&lt;/code&gt;&lt;/pre&gt;\n&lt;hr /&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2019.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;above text from the images is from 4 years ago, like wtf&amp;hellip; I can not grasp time..&lt;/p&gt;\n&lt;p&gt;As i am sitting here writng in this void space at 1:42:14 am, I think to myslef&amp;hellip;wait im drifiting away again..nope. There are lot of distractions during the day time. Sometimes I get the best ideas and solve problems only during the night.. but why.. its not like I can note mute the nose during daytime, I have anc headphones, piano/guitar music, osts, game ost, and etc.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/post-from-august-2020.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;And the above text is from 2020, oh how naive I can be at times? Though I made some progress to contribute to opensource and also progressed in excelling in a new domain. but professionally, I moved to a new company and there I started working in java. Which is not the new domain I told I would pursue. Though for open source I am maintaining some apps for android.&lt;/p&gt;\n&lt;h2 id=&#34;what-am-i-gona-do-this-year&#34;&gt;What am I gona do this year?&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Health&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;During the covid period and after, I became unfit, this i realized after during the tavesl between chennain and hometown, I would feel uneasy flexing hy body and I had no stamina, and when I compared photos, it became obvious.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2020.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;pic from 2021, during my old office&amp;rsquo;s tour - (though I look skinny (lost muscle mass after going to Chennai), I am perfectly healthy) after this I gained weight, developed love-handles which is hardest to lose, as I an writing, drafting this.. I&amp;rsquo;ve already started to workout more, the goal here is to build my old stamina and going back to gym.. yea..back. I used to go to gym on college, at that time I gained weight then when i moved to chennai, lost weight, then when I got back to home during covid, gained a lot of weight..&lt;/p&gt;\n&lt;p&gt;below is the pic of me from 2023 thought I kinda don&amp;rsquo;t look unhealthy, I am unhealthy here, the shirt is covering my belly and love-handles.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/birthday-month-and-reflection/august-post-2023-taken-2023.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;For this year, my goal is to become fit, already on a xx+ day streak at the time of writing this.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Job&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;So here is my goal, I wana move to an even better comapany and expand my knowledge or work here at current compay (vz at the time of writing this) but go to higher designation (SDE II to higher) or even better like my friend who is working remote, find a job that has full time remote. ~review it next year..okey?&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;In terms of Open Source&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;With an office job, social life,..nd all the other distractions.. living at the work location.. its difficult for me to maintain a steady contribution to opensource.. I always think about it, but have somthing that has more priority than this. I guess for the coming year too, it will be the same? idk..lets hope for the best!&lt;/p&gt;\n&lt;h2 id=&#34;additionaly-notes&#34;&gt;Additionaly notes&lt;/h2&gt;\n&lt;p&gt;Removed a lot of stuff after reviewing it a couple of times..but some are encrypted, this was in a draft for a long time now.&lt;/p&gt;</description></item><item><title>Android CI on Github</title><link>//bluepie.in/2023/03/android-ci/</link><pubDate>Sun, 12 Mar 2023 00:08:06 +0000</pubDate><guid>//bluepie.in/2023/03/android-ci/</guid><description>\n&lt;p&gt;Been busy, its not like I lost interest in posting here, I just didn&amp;rsquo;t allocate time for writing here, which is the opposite of what I set out to do.. which in turn does not track my progress at all, I should be held accountable for my progress. ~pff I hate it.&lt;/p&gt;\n&lt;p&gt;A friend told me in the time that I did not post, not necessarily everything should be a progress, maybe this is right or maybe wrong..only time will tell. (Meaning sometimes you can just relax, but to be honest.. I was not releaxing aswell)&lt;/p&gt;\n&lt;h2 id=&#34;why-about&#34;&gt;Why &amp;amp; About&lt;/h2&gt;\n&lt;p&gt;This is going to short, Recently I created a CI for development android builds, this is to reduce &amp;amp; eliminate the manuall build process to every pr, this reduces a lot of time for me.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;so what does this do?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;When a Pull Request is made to &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch, a CI will trigger that will pull all the latest code from that branch and try to build the code in &lt;code&gt;dev&lt;/code&gt; mode, it will report whether tests and build is success and export the final apk. This will be helpful for testers and even me to take the build right away and check whether the code breaks the build or not. Additionally I dont have to manually make a comment everytime for testers to take the build.&lt;/p&gt;\n&lt;h2 id=&#34;android-ci-with-github-actions&#34;&gt;Android CI with github actions&lt;/h2&gt;\n&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate builds, test, and deployment pipeline. We can create workflows that build and test every pull request to our repositories.&lt;/p&gt;\n&lt;h3 id=&#34;github-actions-free-tier&#34;&gt;Github actions free tier&lt;/h3&gt;\n&lt;p&gt;GitHub Actions are unlimited for public repositories and the Free plan also provides 2,000 minutes of Actions each month for use with out private projects.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/github-free-account.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/actions-for-private-reporsitories.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;gradle-build&#34;&gt;Gradle build&lt;/h3&gt;\n&lt;p&gt;There are different variants of your application (release &amp;amp; debug, each variant has its own set of porperties). But what do we need here? debug variant, because that is what we share with the users and get feedback from. I mainly wanted this because everytime I make a pull request, I have to check if it builds, take builds.. I then have to link the builds to that pull request, so others/ even I can use it and test that pull request.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;debug: This is the specific variant of the application that is being built. In Android projects, you can define different build variants, such as debug and release, to configure different settings and behavior. The debug variant is typically used during development and testing, while the release variant is used for production-ready builds. (note that: for the debug build the sizes will be significantly big than release builds)&lt;/p&gt;\n&lt;p&gt;For example: &lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/51fb5782f711388c6632fa2d762b4beded3c9320/app/build.gradle#L19-L33&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;In my case I have these two builds&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-gradle&#34;&gt;buildTypes {\nrelease {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\ndebug {\nminifyEnabled true\nshrinkResources true\nproguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;), &#39;proguard-rules.pro&#39;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I just specifically need the debug build alone.&lt;/p&gt;\n&lt;h2 id=&#34;what-is-a-keystore-why-do-we-need-it&#34;&gt;What is a Keystore &amp;amp; Why do we need it?&lt;/h2&gt;\n&lt;p&gt;A keystore plays a pivotal role in ensuring the security and integrity of our application. Essentially, a keystore is a secure store that holds cryptographic keys, certificates, and sensitive data used to digitally sign our App. These keys are used to digitally sign our App&amp;rsquo;s APK file. This digital signature serves as a means to verify the authenticity and origin of your application. When a user installs an app, the Android system uses the keystore&amp;rsquo;s digital signature to confirm that the App has not been tampered with and that it indeed originates from the expected source. This prevents malicious actors from altering the app&amp;rsquo;s code and introducing potential security vulnerabilities.&lt;/p&gt;\n&lt;p&gt;To simply put, we need this to distribute our app builds, users can trust this build since it is from us.&lt;/p&gt;\n&lt;p&gt;This keystore file should be kept private, should not be shared with others and can not be comitted along with other files in the repository.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;If we keep this file private, how do we take builds then?&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;This is where &lt;a href=&#34;https://docs.github.com/en/actions/security-guides/encrypted-secrets&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;configuring secret in github actions&lt;/a&gt; comes into play.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/configuring-secret-github-actions.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Once the secrets are configured, it can be used like below&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;configuring-the-workflow&#34;&gt;Configuring the workflow&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;We set a name &amp;amp; when should the builds be triggered, it can be of several things Cron, Issue Comment, Pull Request, Fork, Deployment Status..etc. See the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows.&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;full list here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;For my usecase, what I specifically need here is &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;push&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Android CI\non:\npush:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\npull_request:\nbranches: [ &amp;quot;master&amp;quot;, &amp;quot;development&amp;quot; ]\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then we need to &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;specify the jobs&lt;/a&gt;, there can be multiple job, but in our case, we just need a build job, give it a name.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:\nbuild:\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Choosing &lt;a href=&#34;https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;which type of machine&lt;/a&gt; this should be run on.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;runs-on: ubuntu-latest\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;A job contians sequence of tasks called &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;steps&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Checking out&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: checkout &amp;amp; set up JDK 11\n- uses: actions/checkout@v3\nuses: actions/setup-java@v3\nwith:\njava-version: &#39;11&#39;\ndistribution: &#39;temurin&#39;\ncache: gradle\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Decode keystore from repository secrets&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Decode keystore\nenv:\nDEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}\nrun: |\necho &amp;quot;$DEBUG_KEYSTORE&amp;quot; | base64 --decode &amp;gt; debug.keystore\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Make gradle executable &amp;amp; run gradle build&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Grant execute permission for gradlew\nrun: chmod +x gradlew\n- name: Build with Gradle\nenv:\nKEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}\nKEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}\nKEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}\nrun: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;We inject the &lt;code&gt;debug.keystore&lt;/code&gt; &amp;amp; other environment variables into the build using params like &lt;code&gt;-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Upload the artifacts&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- name: Upload debug build\nuses: actions/upload-artifact@v3\nwith:\nname: &amp;lt;upload-name&amp;gt;.apk (build artifact)\npath: app/build/outputs/apk/debug/app-debug.apk\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence/blob/master/.github/workflows/android.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Full workflow file.&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Build in action&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/android-ci/build-in-action.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;This enabled me to not care about dev-debug builds entirely and focus just on development, which is a great time save.&lt;/p&gt;\n&lt;p&gt;In future, I would create a separate releases named &lt;code&gt;nightly&lt;/code&gt;, where the dev branch is built daily and these builds will be updated there.&lt;/p&gt;</description></item><item><title>Updating Dns Servers for Ads and Tracking Protection in 2023</title><link>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</link><pubDate>Sat, 28 Jan 2023 06:02:22 +0000</pubDate><guid>//bluepie.in/2023/01/updating-dns-servers-for-ads-and-tracking-protection-in-2023/</guid><description>\n&lt;h2 id=&#34;upgrading-pi-hole&#34;&gt;Upgrading PI hole&lt;/h2&gt;\n&lt;p&gt;Once every year, I do server maintainence (during Dec-Jan), this year I updated the our dns server from &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;pi-hole&lt;/a&gt; to &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AGH&lt;/a&gt; (both are open source projects..) Just wanted to try an alternative, after using it for a while, the UI &amp;amp; Features on AGH is much better than pi-hole.&lt;/p&gt;\n&lt;h2 id=&#34;why-a-custom-dns-server&#34;&gt;Why a custom dns server?&lt;/h2&gt;\n&lt;p&gt;All websites/apis are just ip addresses (just numbers). Nameservers maintains a mapping of the domain names to the ip addresses, so when we make a request, we get the appropriate ip address.&lt;/p&gt;\n&lt;p&gt;The idea is simple, by running our own dns server, we can prevent our network providers spying on us, on top of that we can intercept &amp;amp; block all ads &amp;amp; trackers.&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;I get ads, but what are Trackers?&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;Similar to ads, ever been in a situation where you searched for something in flipkart/amazon.. then later you find the ad to buy the product elsewhere, this is because of trackers.. trackers are worse because they collect your data, the same way we block ads, trackers should be blocked.&lt;/p&gt;\n&lt;h2 id=&#34;how-this-can-help-us-with-ad-trackers-blocking&#34;&gt;How this can help us with ad &amp;amp; trackers blocking?&lt;/h2&gt;\n&lt;p&gt;Can you make a guess how serving ads in app/websites work? they are from a server too, that server has a domain linked to it.. can you see where I am are going with this?&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;good content -&amp;gt; respond with ip address\nad/tracker -&amp;gt; respond with 0.0.0.0\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Not just blocking ads &amp;amp; tracking, you can do much more.. there are other things that you can do in dns level.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;block windows tracking or updates in windows machines&lt;/li&gt;\n&lt;li&gt;blocking spyware&lt;/li&gt;\n&lt;li&gt;disabling websites for a particular client (ex: blocking instagram on a your phone alone)&lt;/li&gt;\n&lt;li&gt;if you don&amp;rsquo;t want to keep tracking of your watch history in youtube\nand many more.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h3 id=&#34;aren-t-there-other-solutions-to-blocking-trackers-and-ads&#34;&gt;Aren&amp;rsquo;t there other solutions to blocking trackers and ads?&lt;/h3&gt;\n&lt;p&gt;yes but those come with a disadvantage,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Browser extensions.. only applies to browsers (system wide, everywhere and all applications)&lt;/li&gt;\n&lt;li&gt;Android, Iphone (separate apps that use vpn locally to achieve this) - Drains battery alot&lt;/li&gt;\n&lt;li&gt;Rooting android, Jailbreaking ios.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;so this does not require any of that.. no addons, no extra apps, no root, no jailbreak.&lt;/p&gt;\n&lt;h2 id=&#34;dns-over-tls&#34;&gt;DNS Over TLS&lt;/h2&gt;\n&lt;p&gt;Introduced in 2016, main idea is wrapping DNS requests and responses via TLS (Transport Layer Security), ensures observer will not be able to track or tamper the contents of the DNS requests.&lt;/p&gt;\n&lt;!-- ssl - secure socket layer --&gt;\n&lt;!-- tls - tranport layer security, offers end to end security. --&gt;\n&lt;p&gt;Shortly after, android introduced DOT as &amp;ldquo;Private DNS&amp;rdquo; on Android 9 &amp;amp; up, we will be using this feature in our devices to block all ADS &amp;amp; Trackers.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/DOT-noMITM.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;client -&amp;gt; dot server, dot server -&amp;gt; queries the dns.. (we forward our request to serveral dns servers, load-balancers)&lt;/p&gt;\n&lt;p&gt;Traditional DNS vs DOT&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/traditional-vs-dot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;how-do-we-do-it&#34;&gt;How do we do it?&lt;/h2&gt;\n&lt;p&gt;We use an existing open source dns resolver.&lt;/p&gt;\n&lt;p&gt;One options is to go with &lt;a href=&#34;https://github.com/pi-hole/pi-hole&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;PI Hole&lt;/a&gt;, it is a community favourite open source project that has all the bells and whistles for blocking ads and trackers integrated inside it.&lt;/p&gt;\n&lt;p&gt;We will be using &lt;a href=&#34;https://github.com/AdguardTeam/AdGuardHome&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;AG Home&lt;/a&gt;, it is also a popular open source project, which is community maintained. up until lastmonth (dec 2022) I was using PI-hole, switched to AG-Home, just to try something new.&lt;/p&gt;\n&lt;h3 id=&#34;installing&#34;&gt;Installing&lt;/h3&gt;\n&lt;p&gt;I am assuming that you already have a linux box hosted in aws/google/..etc. DOT works on port 853, so you have to allow incoming and outgoing traffic in port 853.&lt;/p&gt;\n&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;\n&lt;p&gt;To make things easier, I will be using docker images to run the software.&lt;/p&gt;\n&lt;p&gt;running a docker container.. command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;\npodman run --name dnsServer\\\n--restart unless-stopped\\\n-p 2346:3000/tcp\\\n-p 1853:853/tcp\\\n-d adguard/adguardhome\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Things to note are, exposed port 3000 is the webport for admin page &amp;amp; 853 is for DOT.&lt;/p&gt;\n&lt;p&gt;We maintain the configs in a yaml file which will be mounted to the container.. here is an example version.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;bind_host: 0.0.0.0\nbind_port: 3000\nbeta_bind_port: 0\nusers:\n# - name: &amp;lt;your-username&amp;gt;\n# password: &amp;lt;your-passowrd&amp;gt;\nauth_attempts: 5\nblock_auth_min: 15\nhttp_proxy: &amp;quot;&amp;quot;\nlanguage: &amp;quot;&amp;quot;\ntheme: auto\ndebug_pprof: false\nweb_session_ttl: 720\ndns:\nbind_hosts:\n- 0.0.0.0\nblocked_response_ttl: 10\nratelimit: 20\nrefuse_any: true\nfastest_timeout: 1s\ntls:\nenabled: true\nserver_name: dns.bluepie.in\nport_https: 443\nport_dns_over_tls: 853\ncertificate_path: /opt/dnsServer/conf/fullchain.pem\nprivate_key_path: /opt/dnsServer/conf/privkey.pem\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Once everything is configured correctly, the service will be up and running.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman ps\nb831cf04743d docker.io/adguard/adguardhome:latest --no-check-update... 1 days ago Up 3 weeks ago 0.0.0.0:1853-&amp;gt;853/tcp, 0.0.0.0:2346-&amp;gt;3000/tcp dnsServer\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;configuring-domain&#34;&gt;Configuring Domain&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;First we need a domain name, a paid one if you have it or a free one would be good, I&amp;rsquo;ll be creating a sub-domain from my domain, (i.e) &amp;lsquo;dns.bluepie.in&amp;rsquo; and get SSL certificates chain for the same.&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Inorder to uniquely identify clients, we need to create a wildcard sud-domain in our DNS provider, in my case godaddy.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/creating-wild-card-dns.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h4 id=&#34;nginx&#34;&gt;Nginx&lt;/h4&gt;\n&lt;p&gt;We will be using nginx as a reverse proxy for exposing our services to the internet.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Admin Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt; server_name dns.bluepie.in;\nlocation / {\nproxy_pass http://localhost:2346/;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_set_header X-Forwarded-Proto $scheme;\nproxy_set_header X-Forwarded-Host $host;\nproxy_redirect / / ;\nproxy_cookie_path / /;\nproxy_connect_timeout 15s;\nproxy_send_timeout 15s;\nproxy_read_timeout 15s;\n}\nlisten 443 ssl;\nssl_certificate /path/to/fullchain.pem;\nssl_certificate_key /path/to/privkey.pem;\ninclude /etc/ssl/options-ssl-nginx.conf;\nssl_dhparam /etc/ssl/ssl-dhparams.pem;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;DNS Config:&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;nginx has a load balancing feature, stream directive to manage tcp and udp. we can use this to forward our requests to dns servers.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-conf&#34;&gt;stream {\nlog_format basic &#39;$time_iso8601 $remote_addr &#39;\n&#39;$protocol $status $bytes_sent $bytes_received &#39;\n&#39;$session_time $upstream_addr &#39;\n&#39;&amp;quot;$upstream_bytes_sent&amp;quot; &amp;quot;$upstream_bytes_received&amp;quot; &amp;quot;$upstream_connect_time&amp;quot;&#39;;\naccess_log /var/log/nginx/dns-access.log basic buffer=1k flush=5s;\nupstream dot {\nzone dot 512k;\nserver 127.0.0.1:1853;\n}\nserver {\nlisten 853;\nproxy_pass dot;\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h3 id=&#34;admin-portal&#34;&gt;Admin portal&lt;/h3&gt;\n&lt;p&gt;&lt;strong&gt;Stats page (configured to refresh every 24 hours)&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-statistics.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Configuring blocklists&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Over the years, I have accustomed to the following blocklists and it works best for me.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dns-blocklists.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Blocklists (entires count taken at the time of writing this post&lt;/strong&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# Blocks Ads, Mobile Ads, Phishing, Malvertising, Malware, Tracking, Telemetry, CryptoJacking, Analytics, Spyware, Ransomware, Exploit, Fraud, Abuse, Scam, Spam, Hijack, Misleading Marketing.\n# ~300,000 entries\nhttps://oisd.nl\n# ~180,000 entries\n# Extending and consolidating hosts files from several well-curated sources like adaway.org, mvps.org, malwaredomainlist.com, someonewhocares.org, and potentially others.\ngithub.com/StevenBlack/hosts\n# ~480,000 entries\n# Automatically updated, moderated and optimized list for blocking ads, trackers and other online garbage.\ngithub.com/notracking/hosts-blocklists\n# Specially Designed for Mobile Ad Protection.\n# ~200,000 entries\ngithub.com/jerryn70/GoodbyeAds\n# Blocking mobile ad providers and some analytics providers.\n# ~7,000 entries\ngithub.com/AdAway/adaway.github.io\n# A filter composed of several other filters (AdGuard Base filter, Social media filter, Tracking Protection filter, Mobile Ads filter, EasyList and EasyPrivacy) and simplified specifically to be better compatible with DNS-level ad blocking.\n# ~50,000 entries\ngithub.com/AdguardTeam/AdguardSDNSFilter\n# Protects you from many types of spyware, reduces bandwidth use, blocks certain pop-up traps, prevents user tracking by way of &amp;quot;web bugs&amp;quot; embedded in spam, provides partial protection to IE from certain web-based exploits and blocks most advertising you would otherwise be subjected to on the internet.\n# ~11,000 entries\nsomeonewhocares.org/hosts\n# Filter that blocks ads on mobile devices. Contains all known mobile ad networks.\n# ~1,000 entries\nkb.adguard.com/general/adguard-ad-filters#mobile-ads-filter\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;how-do-we-use-it&#34;&gt;How do we use it?&lt;/h2&gt;\n&lt;p&gt;Pick a unique word, ex: gopidevice1, then use this word as prefix for &amp;lsquo;dns.bluepie.in&amp;rsquo;. (i.e) &amp;lsquo;gopidevice1.dns.bluepie.in&amp;rsquo;, this is the url that you should use in your devices.&lt;/p&gt;\n&lt;h3 id=&#34;android&#34;&gt;Android&lt;/h3&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/android-config.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;ios-iphone-ipad&#34;&gt;IOS - iphone, ipad&lt;/h3&gt;\n&lt;p&gt;Go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;, navigate to &amp;lsquo;tool&amp;rsquo;, give a name and use your unique url to create a profile.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/ios-config-2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;mac-windows&#34;&gt;Mac/Windows&lt;/h3&gt;\n&lt;p&gt;Same as iphone, go to &lt;a href=&#34;https://dns.bluepie.in/ios/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dns.bluepie.in/ios/&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/block-ads-and-tracking-upgradging-dns-server/dot-install-macos-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;Migrating from PI-Hole to AGH was a good move, once you have used this and experienced &amp;ldquo;no ads&amp;rdquo;, you can not go back~&lt;/p&gt;</description></item><item><title>Modding a macOS App</title><link>//bluepie.in/2022/02/modding-a-macos-app/</link><pubDate>Sun, 20 Feb 2022 19:10:13 +0000</pubDate><guid>//bluepie.in/2022/02/modding-a-macos-app/</guid><description>\n&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;\n&lt;p&gt;This post is about reversing.. but for a MACOS binary. Lets drive straight into it.&lt;/p&gt;\n&lt;p&gt;Modding in macOS is fairly new to me, but a binary is a binary and it can be modded!. I started to daily drive macOS a month or two ago, the why and what&amp;rsquo;s is a whole different story to post about.&lt;/p&gt;\n&lt;p&gt;Like I have said previously in my other &lt;a href=&#34;https://bluepie.in/categories/reversing/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;re posts&lt;/a&gt;, &amp;ldquo; I don&amp;rsquo;t trust mods from the internet&amp;rdquo; If I can mod it then its fine, otherwise I&amp;rsquo;m paying for it.&lt;/p&gt;\n&lt;p&gt;I just started to understand the quirks of &amp;ldquo;OS X&amp;rdquo;, Though some of my methods may be crude&amp;hellip;it works, I will perfect them..&lt;/p&gt;\n&lt;h2 id=&#34;target&#34;&gt;Target&lt;/h2&gt;\n&lt;p&gt;So there is this great app for cleaning your Mac&amp;rsquo;s junk/storage files, but the free version only lets you clean 500mb daily.. the premium version is a whooping 3k per year&amp;hellip;do you get where I am going with this ? &amp;hellip;YES!&amp;hellip;modding for fun and profit (PROFIT = personal use)&lt;/p&gt;\n&lt;h2 id=&#34;tools&#34;&gt;Tools&lt;/h2&gt;\n&lt;p&gt;So I&amp;rsquo;m gonna be trying out ghidra &amp;ldquo; The NSA&amp;rsquo;s reverse engineering tool &amp;ldquo;, mainly because Its a open source tool and I wanted to do some reverse engineering with it for a long time now.&lt;/p&gt;\n&lt;p&gt;make sure you have &lt;a href=&#34;https://brew.sh/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;brew&lt;/a&gt; installed and then run the following command&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-console&#34;&gt;brew install --cask ghidra\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;making-observation-to-the-target&#34;&gt;Making observation to the target&lt;/h2&gt;\n&lt;p&gt;The app throws you with the &amp;ldquo;Nag Screen&amp;rdquo; whenever you use the app, it blocks the usage of features (can not use the app to remove more than 500MB of data,privacy scanning won&amp;rsquo;t work unless you pay,..etc).&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;500mb storage limit reached &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/500mb-over-nag-screen.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Trial period expired &amp;lsquo;nag screen&amp;rsquo;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/clean-my-mac-unmodded.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;basics-understanding-ghidra&#34;&gt;Basics &amp;amp; Understanding Ghidra&lt;/h2&gt;\n&lt;p&gt;All macOS apps have the following structure, the main binary will always be inside the MacOS directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/mac-app-folder-structure.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Launch Ghidra and create a new project, then drag and drop the binary into the project.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-new-project.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Mac has this binary format &amp;ldquo;macobinary&amp;rdquo; - basically it contains the different target executables, ex: (arm binary vs the x86 binary).. mine is a x86 machine, will be modding that alone.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-appropriate-target-binary.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;After the import, Ghidra will automatically ask you to analyse the target binary, choose Analyse. (it will make our life easier), based on the binary size and your cpu cores, it may take couple minutes.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/will-ask-for-analyse-choose-analyse.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Only focus on the following five windows, that is enough to get the job done.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/ghidra-ui.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;1 = will contain the symbol tabel, this is where we do most of the filtering.&lt;/li&gt;\n&lt;li&gt;2 = shows the disassembly of the binary (assembly language), this is where we do the actual reversing, (i.e) patching, understanding logic, etc.&lt;/li&gt;\n&lt;li&gt;3 = This is a great feature, shows the equivalent c code, to the selected assembly section.&lt;/li&gt;\n&lt;li&gt;4 &amp;amp; 5 = shows the incoming and outgoing function calls, this will help us find interesting things about the binary, which then can be used for identifying logic and patching.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;IF ANY OF THE ABOVE WINDOWS ARE NOT OPEN, YOU CAN ENABLE THEM THROUGH THE MENU OPTION &amp;ldquo;WINDOW&amp;rdquo;.&lt;/p&gt;\n&lt;h2 id=&#34;analysis&#34;&gt;Analysis&lt;/h2&gt;\n&lt;p&gt;It will take some time to find what you are looking for, but I have already invested time in this, so what we need is &amp;ldquo;activationManager&amp;rdquo; class, it has all sorts of interesting functions like &amp;ldquo;activationStatus, isAppActivated..etc&amp;rdquo;. Looking for references, it is used throughout the app, so lets change it.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/different-app-status.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Looking around, we find the different status codes inside &amp;ldquo;activationStatus&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;By checking other functions, it becomes obvious that, changing only &amp;ldquo;isAppActivated&amp;rdquo; function is enough. The return type of the function is &amp;ldquo;char&amp;rdquo;, with reference to &amp;ldquo;status&amp;rdquo; from above..we should return &amp;ldquo;3&amp;rdquo;. From the ascii table, &amp;ldquo;0x33&amp;rdquo; is the equivalnet for &amp;ldquo;3&amp;rdquo;.&lt;/p&gt;\n&lt;p&gt;Patching the &amp;ldquo;isAppActivated&amp;rdquo; function, with the following code.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;push rbp\nmov rbp,rsp\nmov DWORD PTR [rbp-0x4],0x0\nmov eax,0x33\npop rbp\nret\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;then export the program &amp;ldquo;File -&amp;gt; Export Program&amp;rdquo;, our patched binary is ready. Now give executable permission and move it to the original directory.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/move-exported-binary-to-original-directory.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;rename the original&lt;/p&gt;\n&lt;h2 id=&#34;signing-running-the-modded-binary-for-profit&#34;&gt;Signing &amp;amp; Running the modded binary for PROFIT&lt;/h2&gt;\n&lt;p&gt;Unlike windows and linux, macOS won&amp;rsquo;t run applications without signing the binary.. but we can generate your own certificates for personal use (dont have to buy a developer account for 99$/year).&lt;/p&gt;\n&lt;p&gt;create a certificate using &amp;ldquo;keychain Access&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/create-custom-certifcate.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;choose &amp;ldquo;code signing&amp;rdquo; in &amp;ldquo;certificate type&amp;rdquo;&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/choose-code-signing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;Go to the application root &amp;ldquo;/Application/&amp;rdquo; to resign the entire app.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo codesign -s &amp;quot;&amp;lt;name-of-certificate&amp;gt;&amp;quot; -fv CleanMyMac-MAS.app/\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Run the app&amp;hellip; Now its been modded and I don&amp;rsquo;t have to pay 3k INR a year or worry about untrusted binaries from the internet.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/modding-a-macos-app/removed-files-beyong-the-limit.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;You can see from the image, all the premium features are now unlocked &amp;amp; we can also remove the junk files exceeding 500MB.&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;This was a fun excerise to dig around. Macos is not that limited as I thought it was&amp;hellip; Sure it has its quirks.&lt;/p&gt;</description></item><item><title>&#34;F-droid&#34; vs the pain that is &#34;Google PlayStore&#34;</title><link>//bluepie.in/2022/01/publishing-to-fdroid-google-play/</link><pubDate>Wed, 05 Jan 2022 21:34:18 +0530</pubDate><guid>//bluepie.in/2022/01/publishing-to-fdroid-google-play/</guid><description>\n&lt;p&gt;&lt;strong&gt;How you doing? hope you are doing well. I missed to post on december because I was juggling too much things at once and needed a break&lt;/strong&gt;&lt;/p&gt;\n&lt;h2 id=&#34;fdroid&#34;&gt;Fdroid?&lt;/h2&gt;\n&lt;p&gt;&lt;a href=&#34;https://fdroid.org&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Fdroid&lt;/a&gt; is a free and open source Android application repository. You can use Fdroid via the &lt;a href=&#34;https://f-droid.org/client&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;F-Droid client&lt;/a&gt; or the &lt;a href=&#34;https://f-droid.org/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;F-Droid website&lt;/a&gt;. It is similar to the &lt;a href=&#34;https://play.google.com/store&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google Play Store&lt;/a&gt;, but with a few key differences:&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;is free and open source&lt;/li&gt;\n&lt;li&gt;contains only open source applications&lt;/li&gt;\n&lt;li&gt;is not a commercial service&lt;/li&gt;\n&lt;li&gt;all google play dependencies are stripped out when publishing&lt;/li&gt;\n&lt;li&gt;goes through a series of automated tests &amp;amp; manual reviews before being accepted&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/fdroid.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h3 id=&#34;how-do-i-add-a-new-app-to-fdroid&#34;&gt;How do I add a new app to Fdroid?&lt;/h3&gt;\n&lt;p&gt;There are two options, choose one:&lt;/p&gt;\n&lt;p&gt;If you don&amp;rsquo;t want to do it yourself&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;You create a issue in &lt;a href=&#34;https://gitlab.com/fdroid/rfp/-/issues&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;REP - Request For Packaging&lt;/a&gt; and the contributors will review your request and add it to the repository.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/rep.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;If you want to do it yourself&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Open a issue on &lt;a href=&#34;https://gitlab.com/fdroid/fdroiddata/-/merge_requests&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Fdroid data&lt;/a&gt; with the appropriate yaml(metadata) file, you should create one with your metadata, refer &lt;a href=&#34;https://f-droid.org/docs/FAQ_-_App_Developers/#how-do-i-get-my-app-included&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this&lt;/a&gt;, &lt;a href=&#34;https://f-droid.org/docs/Submitting_to_F-Droid_Quick_Start_Guide/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this link&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://gitlab.com/fdroid/fdroiddata/blob/master/CONTRIBUTING.md#merge-requests&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this&lt;/a&gt; for more information.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;After being reviewed and added to the repository, it will avialable for everyone to download and install.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/fdroid-data.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;p&gt;here is a basic example of the metadata yaml file:&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;Categories:\nLicense:\nAuthorName:\nAuthorEmail:\nAuthorWebSite:\nWebSite:\nSourceCode:\nIssueTracker:\nChangelog:\n...etc\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;View the full file &lt;a href=&#34;https://gitlab.com/fdroid/wiki/-/wikis/Metadata/YAML-Metadata&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;&lt;/p&gt;\n&lt;h3 id=&#34;how-do-i-publish-updates-to-fdroid&#34;&gt;How do I publish updates to Fdroid?&lt;/h3&gt;\n&lt;p&gt;The great thing about Fdroid is that it automatically detects new versions of your app from github and updates it there. So you just have to make a new tag in your repo and fdroid will handle the rest.&lt;/p&gt;\n&lt;h3 id=&#34;how-did-i-publish-to-fdroid&#34;&gt;How did I publish to Fdroid?&lt;/h3&gt;\n&lt;p&gt;I went through &amp;ldquo;do it yourself&amp;rdquo; and &lt;a href=&#34;https://gitlab.com/fdroid/fdroiddata/-/merge_requests/10097&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;this is the pull request&lt;/a&gt; that I created.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;AntiFeatures:\n- NonFreeAdd\nCategories:\n- System\nLicense: GPL-3.0-only\nAuthorName: BluePie\n...etc\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;View the full file is &lt;a href=&#34;https://gitlab.com/bluepie/fdroiddata/-/raw/09cc09572b0352a07b84a01a9d21ae5f4c4a1bc3/metadata/bluepie.ad_silence.yml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;&lt;/p&gt;\n&lt;h2 id=&#34;google-play-store&#34;&gt;Google Play Store?&lt;/h2&gt;\n&lt;p&gt;Goople PlayStore needs no explanation. It is a commercial service that is used by millions of users to download and install apps. It is a great way to monetize &amp;amp; distribute your apps. You need to buy a license with a one time fee though.&lt;/p&gt;\n&lt;p&gt;Anyway lets get started&amp;hellip;&lt;/p&gt;\n&lt;h3 id=&#34;how-do-i-publish-update-to-google-play-store&#34;&gt;How do I publish/Update to Google Play Store?&lt;/h3&gt;\n&lt;ul&gt;\n&lt;li&gt;First you create a new app,&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/create-new-app.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Complete all the tasks in the Dashboard&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/complete-tasks-in-console-dashboard.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Generate privacy policy(&lt;a href=&#34;https://app-privacy-policy-generator.firebaseapp.com&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;i recommend this&lt;/a&gt;) and host it somewhere.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/generate-privacy-policy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Take build bundle in android studio.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/generate-signed-apk-bundle.png&#34; alt=&#34;&#34; /&gt;\n&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/generate-bundle-dialog.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Create store listing with appropriate data,&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/update-store-listing.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;Note to upload the correct images&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/upload-appropriate-images.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;use the bundle from before and make a test release and promote it to production. (review process takes time)&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/upload-bundle.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;the-pain&#34;&gt;The pain&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;Publishing to playstore especially when the app is OpenSource is a manual time consuming process vs the automated process in Fdroid.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;There is a onetime fee for publishing to playstore.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Every release takes a week to get reviewed and published, even if it is OpenSource.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;from my experience time differs between minor, major and bug fixes (based on semantic versioning)&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;The customer support is full of bots&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;Store is unfair&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;I got my app delisted because I had the word &amp;ldquo;AD&amp;rdquo; in the Icon.. eventhough there are other apps from playstore with 1m+ installs that also has the same, either way I had to change the Icon to get it back on the store.&lt;/li&gt;\n&lt;li&gt;I could not get a proper response from Support &lt;a href=&#34;https://github.com/aghontpi/ad-silence/issues/19&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;more on here&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;The feedback back system is not that great, If users encounter crash/ something is not working.. In github you can have a correct feedback loop but in playstore..its just an uninstall with a lower rating.&lt;/p&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/publishing-to-fdroid-and-playstore/delisted.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;h2 id=&#34;end&#34;&gt;End&lt;/h2&gt;\n&lt;p&gt;If you have an OpenSource App, spend less time updating it on Playstore. Stick with Github releases or Fdroid releases. Don&amp;rsquo;t even get my started on Apple app store.&lt;/p&gt;</description></item><item><title>Accuradio, Spotify...etc Adblocker in Kotlin</title><link>//bluepie.in/2021/11/accuradio-spotify-adblocker-in-kotlin/</link><pubDate>Thu, 18 Nov 2021 19:46:00 +0000</pubDate><guid>//bluepie.in/2021/11/accuradio-spotify-adblocker-in-kotlin/</guid><description>\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;Hello how you been?, hope everything is going well.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;I wanted to learn Kotlin for native Android development, I already knew how to do it with java, so the only obstacle for me is learning Kotlin.&lt;/p&gt;\n&lt;h2 id=&#34;kotlin&#34;&gt;Kotlin&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;Kotlin &lt;a href=&#34;https://trello.com/c/zYFtp51G/1-learn-kotlin&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;trello board&lt;/a&gt; that I used to track progress.&lt;/li&gt;\n&lt;li&gt;After learning the basics for Kotlin, &lt;a href=&#34;https://play.kotlinlang.org/koans/overview&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Kotlin koans&lt;/a&gt; is a great place for practice (beginner to advanced).&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;chosing-what-to-do-with-kotlin&#34;&gt;Chosing what to do with Kotlin&lt;/h2&gt;\n&lt;p&gt;I use &lt;a href=&#34;https://accuradio.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Accuradio&lt;/a&gt; android app to stream music, but it does not have a paid tear, So the ads are not skippable and there was no existing adblocker that does this(which is a pain). So I decided to write one in Kotlin.&lt;/p&gt;\n&lt;p&gt;So its decided, I am doing a project in Kotlin to block/silence ads in Accuradio, some additional things I decided to implement are the following.&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;should be extensible to other apps like Spotify,&amp;hellip;etc&lt;/li&gt;\n&lt;li&gt;should have minimal UI&lt;/li&gt;\n&lt;li&gt;should be lightweight&lt;/li&gt;\n&lt;li&gt;should be completely openSource.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;To be extensbile, I should have a common pattern/way to detect ads in these apps&amp;hellip; One such way is by using app&amp;rsquo;s notification.&lt;/p&gt;\n&lt;p&gt;I can determine what an app is currenly playing using the notification, using the &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;notification listener&lt;/a&gt; service and parsing the posted notifications, mute/unmute the app based on the content.&lt;/p&gt;\n&lt;p&gt;&lt;img src=&#34;//bluepie.in/images/posts/accuradio-spotify-ad-blocker/flow.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;An update, &lt;strong&gt;I already added support for Spotify and Tidal.&lt;/strong&gt;&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;h2 id=&#34;implementation&#34;&gt;Implementation&lt;/h2&gt;\n&lt;p&gt;I pretty much have to just&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;implement the &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;NotificationListenerService&lt;/a&gt; service, and parse the posted notifications,&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;extend the &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;NotificationListenerService&lt;/a&gt; class and implement the &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService.OnNotificationPostedListener.html#onNotificationPosted&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;onNotificationPosted&lt;/a&gt; method &amp;amp; &lt;a href=&#34;https://developer.android.com/reference/android/app/NotificationListenerService.OnNotificationConnectedListener.html#onNotificationConnected&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;onNotificationConnected&lt;/a&gt; method.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-kt&#34;&gt;class NotificationListener : NotificationListenerService() {\noverride fun onListenerConnected() {\nsuper.onListenerConnected()\nappNotificationHelper?.updateNotification(&amp;quot;AdSilence, listening for ads&amp;quot;)?.run {\nstartForeground(NOTIFICATION_ID, this) // persistent notification\n}\nLog.v(TAG, &amp;quot;notification listener connected&amp;quot;)\n}\noverride fun onNotificationPosted(sbn: StatusBarNotification?) {\nsuper.onNotificationPosted(sbn)\n}\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;why do I need to run it as a foreground service?&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;because In recent versions of android, any app that is actively running the background must post a notification to the status bar. Otherwise the app will be killed by the system.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;then mute/unmute the app based on the content,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-kt&#34;&gt;\nsbn?.let {\nwith(AppNotification(applicationContext, it.notification, sbn.packageName)) {\nPreference(applicationContext).isAppConfigured(this.getApp()).takeIf { b -&amp;gt; b }\n?.run {\nUtils().run {\nwhen (NotificationParser(this@with).isAd()) {\ntrue -&amp;gt; {\nthis.mute()\nisMuted = true\n}\nfalse -&amp;gt; {\nisMuted = false\nthis.unmute();\n)\n}\n}\n}\n}\n}\n}\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;next important step is granting the app, the permission to listen to notifications. (if all apps can listen to incoming notifications, this would be a huge security flaw!)&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://developer.android.com/training/permissions/requesting&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;grant permission&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;since this app is openSource, everyone can see the source code before granting permission&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;bascially the following one line has to be used. (note: using string to support older devices)&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-kotlin&#34;&gt;startActivity(Intent(&amp;quot;enabled_notification_listeners&amp;quot;))\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;in the android manifest, add the following to the &lt;code&gt;&amp;lt;application&amp;gt;&lt;/code&gt; tag,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;telling the system to use the file &lt;code&gt;NotificationListenerService&lt;/code&gt; as the notification listener service.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;service android:name=&amp;quot;.NotificationListener&amp;quot;\nandroid:label=&amp;quot;@string/notification_listener_name&amp;quot;\nandroid:permission=&amp;quot;android.permission.BIND_NOTIFICATION_LISTENER_SERVICE&amp;quot;\nandroid:exported=&amp;quot;false&amp;quot;&amp;gt;\n&amp;lt;intent-filter&amp;gt;\n&amp;lt;action android:name=&amp;quot;android.service.notification.NotificationListenerService&amp;quot; /&amp;gt;\n&amp;lt;/intent-filter&amp;gt;\n&amp;lt;/service&amp;gt;\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;That is all is required to get the app to work, there are other things like UI, checking app installed or not, maintaining the app running in the background, hiding the app from recents, restarting the service, preference&amp;hellip;etc. Since the app is so lightweight (around 150KB)&amp;hellip;&lt;/p&gt;\n&lt;p&gt;I could write about every part of the app in this, but this app is open source&amp;hellip;.its already explained. It was so refreshing and fun work on this&amp;hellip; Kotlin is fun and I am glad to have learned it(atleast some bit).&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/aghontpi/ad-silence&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Github/source code&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://play.google.com/store/apps/details?id=bluepie.ad_silence&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Google play store link&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://f-droid.org/packages/bluepie.ad_silence/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;Fdroid link&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;hr /&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://youtu.be/hWfiQN4IUFM&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;featured in top apps - youtube&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;&lt;a href=&#34;https://www.nextpit.com/apps-of-the-week-48-2021&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;featured in app of the week&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;</description></item><item><title>Open Source Dnd Email Editor</title><link>//bluepie.in/2021/10/open-source-dnd-email-editor/</link><pubDate>Sun, 10 Oct 2021 10:48:30 +0000</pubDate><guid>//bluepie.in/2021/10/open-source-dnd-email-editor/</guid><description>\n&lt;p&gt;I created a drag and drop email editor, I&amp;rsquo;ve been working on it for the past month. It&amp;rsquo;s a lot of fun to work on, I completed it for first release, I&amp;rsquo;ll shed some light on the challenges I faced and how I solved them.&lt;/p&gt;\n&lt;h2 id=&#34;why-s-what-s&#34;&gt;Why&amp;rsquo;s &amp;amp; What&amp;rsquo;s&lt;/h2&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;why build this?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;No free editors/open source editors that does the job,&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;but why not profit?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Not interested, I believe in Open Source. Heavily inspired by how LINUX (OPEN SOURCE) is useful for everyone now.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;the real motivation?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Let me be frank here, I dont like &lt;em&gt;unalyer github&lt;/em&gt;, MAINLY BECAUSE OF THEIR FALSE ADVERTISEMENT AS OPEN SOURCE, I understand that&amp;rsquo;s how the get their business going but, damn its scummy.&lt;/p&gt;\n&lt;p&gt;One by one respoectively&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;scumbag unlayer,&lt;/li&gt;\n&lt;li&gt;grapjs mjml,\n&lt;ul&gt;\n&lt;li&gt;&lt;a href=&#34;https://github.com/artf/grapesjs-mjml&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;their implementation&lt;/a&gt; was cool with mjml&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;company that I am working for, at the time of writing this, needed an email dnd editor.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;h2 id=&#34;challenges&#34;&gt;Challenges&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;maintaining layer between MJML and Me.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;writing parsers, serializers and deseriealizers.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;design and code ui&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;manage drag and drop, mouse, key events, &amp;hellip;etc.&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;deciding what kind of api to expose&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;deciding what inline editor to use&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;I can do one of two things&lt;/li&gt;\n&lt;li&gt;One, spend time &amp;amp; learn the api&amp;rsquo;s of an existing inline editor&lt;/li&gt;\n&lt;li&gt;Two, spend time creating a yet another inline editor, that I have full control of. (I&amp;rsquo;ll learn a lot of new things If I do this)&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I did both, but after finding serveral small size inline editors, I decided to completely go ahead with creating a custom InlineEditor, it is not a major task if I used &lt;em&gt;document.executeCommand&lt;/em&gt;&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;maintaining demo/docs/npm package in single repo&lt;/p&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;backend&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;using firebase fn in combination with express&lt;/li&gt;\n&lt;li&gt;firestore for database&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;I&amp;rsquo;ll explain &lt;strong&gt;&lt;em&gt;maintaining demo/docs/npm package in single repo&lt;/em&gt;&lt;/strong&gt; now, since that is the recent &amp;amp; last thing that I completed.&lt;/p&gt;\n&lt;h2 id=&#34;maintaining-demo-docs-npm-package-in-single-repo&#34;&gt;Maintaining demo/docs/npm package in single repo&lt;/h2&gt;\n&lt;p&gt;Let me fist explain the layout of the items in the repo.&lt;/p&gt;\n&lt;p&gt;the repo contains,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;em&gt;create-react-app&lt;/em&gt; react-scripts by default for the demo page&lt;/li&gt;\n&lt;li&gt;the &lt;em&gt;editor&lt;/em&gt; component\n&lt;ul&gt;\n&lt;li&gt;I want this to be bundled and published to npm&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;the demo branch, that automatically deploys with &lt;a href=&#34;https://pages.github.com/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github pages&lt;/a&gt;&lt;/li&gt;\n&lt;li&gt;api docs for the project&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;checklist that I created before starting work on this&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Its always better to plan ahead to avoid surprises later, created a small checklist to help me with this.&lt;/p&gt;\n&lt;p&gt;I should have,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;verified that the Editor is standalone\n&lt;ul&gt;\n&lt;li&gt;(i.e). that there is no demo build dependencies directly in the editor (like on first editor load, fetch this template from the backend, ..etc)&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;verified that the Editor is fully functional\n&lt;ul&gt;\n&lt;li&gt;(i.e). that the editor can be used in a demo page without problems&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;decided on what build system to use\n&lt;ul&gt;\n&lt;li&gt;webpack&lt;/li&gt;\n&lt;li&gt;rollup&lt;/li&gt;\n&lt;li&gt;nd&amp;hellip;etc, millions of other build systems out there&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;created the build system, in such a way that It wont affect the demo build (created with create-react-app)\n&lt;ul&gt;\n&lt;li&gt;at the time of writing this, react-scripts uses webpack v4, So installing another instance of webpack v5 for npm build, will cause react-scripts to fail, the same goes for its dependencies aswell.&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;created a seperate branch, do all documentations and changes in that branch&lt;/li&gt;\n&lt;li&gt;bundled only selected files in the npm build, should only contain\n&lt;ul&gt;\n&lt;li&gt;editor&lt;/li&gt;\n&lt;li&gt;css/sass files used with the editor&lt;/li&gt;\n&lt;li&gt;image files used with the editor&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;created sepereate &lt;strong&gt;&lt;em&gt;scripts in package.json&lt;/em&gt;&lt;/strong&gt; for the differnet builds\n&lt;ul&gt;\n&lt;li&gt;build = for react-scripts-build&lt;/li&gt;\n&lt;li&gt;build:module = for npm-build&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;created a seperate packge.json for the npm build, since the exported Editor component doesn&amp;rsquo;t need all the dependencies from the repository.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;Choosing build system for the editor&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;I have already published packages using webpack in the past, So I already knew webpack and its different modules. But the problem is I don&amp;rsquo;t know if I can use two webpack instances in a single project.&lt;/p&gt;\n&lt;p&gt;I Could go for a new build system just to solve this problem alone, Or find a way to use two webpack instances in a single project.&lt;/p&gt;\n&lt;p&gt;This is the point in time, where I found out about &lt;a href=&#34;https://classic.yarnpkg.com/lang/en/docs/cli/add/#toc-yarn-add-alias&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;&lt;em&gt;yarn alias&lt;/em&gt;&lt;/a&gt;, After I found this, I knew which build system to choose (webpack v5).&lt;/p&gt;\n&lt;p&gt;webpack has an environment configuration named &amp;lsquo;WEBPACK_PACKAGE&amp;rsquo;, it takes the package name of the webpack, So I can utilize this to install multiple versions, one with name &amp;lsquo;webpack&amp;rsquo; another with alias &amp;lsquo;webpack-module&amp;rsquo;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;webpack-module&amp;quot;: &amp;quot;npm:webpack@^5.60.0&amp;quot;\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I can then run webpack normally with &amp;lsquo;react-scripts&amp;rsquo; and &amp;lsquo;WEBPACK_PACKAGE=webpack-module webpack&amp;rsquo; for npm build&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;Requirements for publishing to npm&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;For publishing to npm, I need,&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;npm account&lt;/li&gt;\n&lt;li&gt;a package.json with seperate dependencies from the demo package.json\n&lt;ul&gt;\n&lt;li&gt;there should be a two package.json now. One for demo build, another for package build&lt;/li&gt;\n&lt;/ul&gt;&lt;/li&gt;\n&lt;li&gt;a webpack configuration file for bundling&lt;/li&gt;\n&lt;li&gt;typescript definition file&lt;/li&gt;\n&lt;li&gt;output of &lt;em&gt;npm pack&lt;/em&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;Webpack configuration&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;webpack configuration should process&lt;/p&gt;\n&lt;ul&gt;\n&lt;li&gt;process scss, css and module css, scss.&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;&lt;a href=&#34;https://webpack.js.org/loaders/style-loader/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;style loader&lt;/a&gt; - Inject the bundled styles into webpage.&lt;/p&gt;\n&lt;p&gt;since I used &lt;em&gt;@import&lt;/em&gt; for fonts and &lt;em&gt;url&lt;/em&gt; for background, I need to add &lt;a href=&#34;https://webpack.js.org/loaders/css-loader/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;css loader&lt;/a&gt;&lt;/p&gt;\n&lt;p&gt;I have also used sass, thus &lt;a href=&#34;https://webpack.js.org/loaders/sass-loader/&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;sass loader&lt;/a&gt; is required. To note, for the modern web, there is &lt;a href=&#34;https://github.com/sass/dart-sass&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;dart sass&lt;/a&gt;. I can &lt;a href=&#34;https://webpack.js.org/loaders/sass-loader/#object&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;instruct &amp;ldquo;sass loader&amp;rdquo; to use &amp;ldquo;dart sass&amp;rdquo;&lt;/a&gt;&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;// css\n{\ntest: /(\\.css$|\\.module\\.css$)/i,\nuse: [&#39;style-loader-module&#39;, &#39;css-loader-module&#39;],\n},\n// scss\n{\ntest: /(\\.s[ac]ss$|\\.module\\.s[ac]ss$)/i,\nuse: [\n{\nloader: &#39;style-loader-module&#39;,\n},\n{\nloader: &#39;css-loader-module&#39;,\noptions: {\nimportLoaders: 1,\nmodules: {\nmode: &#39;local&#39;,\n},\n},\n},\n{\nloader: &#39;sass-loader-module&#39;,\noptions: {\nimplementation: require(&#39;sass&#39;),\n},\n},\n],\n}\n&lt;/code&gt;&lt;/pre&gt;\n&lt;ul&gt;\n&lt;li&gt;&lt;p&gt;process .tsx, .ts and .jsx&amp;hellip;etc.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{\ntest: /\\.(js|jsx|ts|tsx)$/,\nexclude: /node_modules/,\nuse: {\nloader: &#39;babel-loader-module&#39;,\noptions: {\npresets: [\n&#39;@babel/preset-env&#39;,\n[&#39;@babel/preset-react&#39;, { runtime: &#39;automatic&#39; }],\n&#39;@babel/preset-typescript&#39;,\n],\n},\n}\n}\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;instruct webpack to recognize .tsx file extensions.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;resolve: {\nextensions: [&#39;.js&#39;, &#39;.jsx&#39;, &#39;.ts&#39;, &#39;.tsx&#39;],\n}\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;li&gt;&lt;p&gt;a way to make image assets work in extensions,In webpack 5, I can bundle images inline in js files.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{\ntest: /\\.svg/,\ntype: &#39;asset/inline&#39;,\n}\n&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;\n&lt;/ul&gt;\n&lt;p&gt;That is it for the webpack config, Isn&amp;rsquo;t this simple and concise.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;Maintaining another package.json for npm&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;Obviously, only at the time of packing I need package.json untill then I can renaming to something else, I named it to &amp;lsquo;module-package.json&amp;rsquo; then at time of &amp;lsquo;bundling and packing&amp;rsquo;, I rename it to &amp;lsquo;package.json&amp;rsquo;&lt;/p&gt;\n&lt;p&gt;the contents of &amp;lsquo;module-package.json&amp;rsquo; should contain the following,&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt; &amp;quot;name&amp;quot;: &amp;quot;dnd-email-editor&amp;quot;,\n&amp;quot;version&amp;quot;: &amp;quot;v0.0.0-alpha.3&amp;quot;,\n&amp;quot;description&amp;quot;: &amp;quot;Drag and Drop Editor designed for mails&amp;quot;,\n&amp;quot;main&amp;quot;: &amp;quot;./main.js&amp;quot;,\n&amp;quot;files&amp;quot;: [&amp;quot;**/*&amp;quot;],\n&amp;quot;types&amp;quot;: &amp;quot;./index.d.ts&amp;quot;,\n&amp;quot;peerDependencies&amp;quot;: {\n&amp;quot;react&amp;quot;: &amp;quot;^17.0.1&amp;quot;,\n&amp;quot;react-dom&amp;quot;: &amp;quot;^17.0.1&amp;quot;\n},\n&amp;quot;author&amp;quot;: &amp;quot;https://github.com/aghontpi&amp;quot;,\n&amp;quot;license&amp;quot;: &amp;quot;GPL-3.0&amp;quot;,\n&amp;quot;bugs&amp;quot;: {\n&amp;quot;url&amp;quot;: &amp;quot;https://github.com/aghontpi/dnde/issues&amp;quot;\n},\n&amp;quot;homepage&amp;quot;: &amp;quot;https://github.com/aghontpi/dnde&amp;quot;,\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;On every version change, change &amp;ldquo;version&amp;rdquo; key, It&amp;rsquo;ll automatically update in &lt;a href=&#34;https://npmjs.com&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;npmjs&lt;/a&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;&lt;em&gt;bundling and packing&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;\n&lt;p&gt;I added the following command to package.json, (original package.json not &amp;lsquo;module-package.json&amp;rsquo;), note the environment variable &amp;lsquo;WEBPACK_PACKAGE&amp;rsquo; I mentioned from before.&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;WEBPACK_PACKAGE=webpack-module webpack &amp;amp;&amp;amp;\ncp module-package.json module/package.json &amp;amp;&amp;amp;\ncp index.d.ts module/index.d.ts &amp;amp;&amp;amp;\ncp README.md module/README.md &amp;amp;&amp;amp;\nnpm pack ./module/\n&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;I can the publish the output from the previous command with&lt;/p&gt;\n&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;npm publish build-verion.tgz\n&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;\n&lt;p&gt;I oversimplified in many cases but this covers &amp;lsquo;publishing to npm part&amp;rsquo;. I solved a lot of similar interesting problems in the project, Check it &lt;a href=&#34;https://github.com/aghontpi/dnde&#34; rel=&#34;nofollow noreferrer&#34; target=&#34;_blank&#34;&gt;github dnde.&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>"}}}}