<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[A.D Works]]></title><description><![CDATA[A running series exploring free, open-source tools that quietly replace everyday paid apps - from password managers to note taking, photo backups, and files sync. No subscriptions, no data lock-in, full control]]></description><link>https://adworks.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6a9d51b62ac5ce07f583344b/df7dd5f3-d959-4ecc-92f7-043ba8ee20c0.png</url><title>A.D Works</title><link>https://adworks.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 16 Sep 2026 20:10:55 GMT</lastBuildDate><atom:link href="https://adworks.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Password Manager That Never Touches the Internet]]></title><description><![CDATA[I'd already switched my daily password manager to a self-hosted option, and I was happy with it. But there was one account that kept nagging at me every time I logged into it: my primary email. The on]]></description><link>https://adworks.hashnode.dev/the-password-manager-that-never-touches-the-internet</link><guid isPermaLink="true">https://adworks.hashnode.dev/the-password-manager-that-never-touches-the-internet</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Mon, 14 Sep 2026 16:30:21 GMT</pubDate><content:encoded><![CDATA[<p>I'd already switched my daily password manager to a self-hosted option, and I was happy with it. But there was one account that kept nagging at me every time I logged into it: my primary email. The one account that, if compromised, could be used to reset almost every other password I owned.</p>
<p>I found myself asking an odd question: did even a self-hosted vault need to be online at all, for something this sensitive? My server was mine, encrypted, and reasonably secure — but it was still a service running somewhere, reachable over a network, with all the small risks that implies. For my most critical handful of accounts, I wanted something that couldn't be touched over a network at all, because it simply wasn't on one.</p>
<h2>Looking for "Offline" as a Feature, Not a Limitation</h2>
<p>Most password managers treat "offline" as something to work around — a fallback mode for when you lose connectivity, not a design choice. I wanted the opposite: a tool built from the ground up to never need a network connection in the first place.</p>
<p>That search led me to <strong>KeePassXC</strong>, an open-source password manager with a completely different model than most. There's no server. No account to create. No sync service baked in. Your entire vault is a single encrypted file, sitting wherever you put it, and the only way in is your master password (optionally combined with a key file or hardware security key).</p>
<h2>Setting It Up</h2>
<p>Installing KeePassXC felt almost anticlimactic after wrestling with servers and certificates for other tools. Download it, open it, create a new database, set a strong master password. That's it. No account creation screen. No email verification. No terms of service to scroll past. The vault file it created lived exactly where I told it to — a local folder, nothing more.</p>
<p>I moved my most sensitive credentials into it: my primary email, my domain registrar, my banking logins, the handful of accounts where "if this is compromised, everything else falls too" applies. Each entry encrypted inside a single <code>.kdbx</code> file that means nothing to anyone without my master password.</p>
<h2>The Auto-fill Surprise</h2>
<p>I'd assumed "offline password manager" meant "manually copy-pasting passwords forever," and I was ready to accept that trade-off for my most sensitive accounts. I was wrong. KeePassXC has a browser integration that handles auto-fill just like any cloud-based manager — the browser extension talks to the locally running KeePassXC app, which unlocks the relevant entry only when I approve it. The convenience I expected to lose, I mostly kept.</p>
<h2>The Part That Actually Changed My Thinking</h2>
<p>A few weeks in, I needed to log into that primary email account from a different laptop while traveling. My cloud-synced vault would have handled that instantly. KeePassXC didn't, because the vault file simply wasn't on that machine — and for a second, that felt like a limitation.</p>
<p>Then I realized: that's exactly what I'd wanted. The accounts I'd put in KeePassXC were the ones I deliberately didn't want accessible from just any device, anywhere, the moment I typed a master password. I ended up copying the file over manually via a USB drive, and for these specific accounts, that small bit of friction felt like a feature rather than an inconvenience.</p>
<p>For everyday, lower-stakes accounts, I still use my synced vault. For the handful of accounts that really matter, KeePassXC's offline-by-default approach has become the deliberate extra step between "convenient" and "compromised."</p>
<h2>What My Setup Actually Looks Like Now</h2>
<ul>
<li>A self-hosted, synced vault for daily accounts — subscriptions, shopping sites, general logins</li>
<li>A completely offline KeePassXC vault for the handful of accounts that could unravel everything else if breached</li>
<li>Browser auto-fill still works for both, just through different mechanisms</li>
<li>The offline vault gets backed up manually, on my terms, to storage I control directly — no automatic cloud sync at all</li>
</ul>
<h2>The Bigger Lesson</h2>
<p>Not every account needs the same level of protection, and not every password manager needs to optimize purely for convenience. Some accounts are worth the extra ten seconds of friction in exchange for a vault that, by design, was never reachable over a network to begin with.</p>
<p>KeePassXC didn't replace my everyday password manager. It gave me a second, deliberately harder-to-reach option for the accounts where "harder to reach" is exactly the point.</p>
<hr />
<p><em>This is part of my "Everyday Open Source" series, where I highlight open-source tools that quietly make daily life better.</em></p>
<h2>Try It Yourself</h2>
<ul>
<li><strong>Website:</strong> <a href="https://keepassxc.org">keepassxc.org</a></li>
<li><strong>GitHub (source code):</strong> <a href="https://github.com/keepassxreboot/keepassxc">github.com/keepassxreboot/keepassxc</a></li>
<li><strong>Documentation:</strong> <a href="https://keepassxc.org/docs/">keepassxc.org/docs</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The App I Didn't Realize Was Selling My Attention]]></title><description><![CDATA[I needed to download a Linux distribution ISO — a large file, a few gigabytes, the kind of thing torrenting was actually built for. I opened the torrent client I'd been using for years out of habit, c]]></description><link>https://adworks.hashnode.dev/the-app-i-didn-t-realize-was-selling-my-attention</link><guid isPermaLink="true">https://adworks.hashnode.dev/the-app-i-didn-t-realize-was-selling-my-attention</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Sun, 13 Sep 2026 07:39:29 GMT</pubDate><content:encoded><![CDATA[<p>I needed to download a Linux distribution ISO — a large file, a few gigabytes, the kind of thing torrenting was actually built for. I opened the torrent client I'd been using for years out of habit, clicked download, and watched as three separate ad banners loaded around the interface before the download even started.</p>
<p>I sat there for a second, genuinely annoyed. I wasn't trying to stream a movie or download anything questionable — I was grabbing an official, legal open-source operating system image, and I still had to sit through ads bolted onto a client I'd never actually chosen, just inherited from some "recommended download" years ago.</p>
<p>That was the moment I actually looked at what else was out there.</p>
<h2>What I Didn't Know I'd Accepted</h2>
<p>Torrenting, at its core, is just a protocol — a decentralized way to distribute files by sharing pieces of them across many people at once, instead of everyone hammering a single server. It's how a lot of legitimate large files move: Linux distributions, open-source software releases, public domain media, game patches from certain platforms, scientific datasets shared by research institutions.</p>
<p>The protocol itself is neutral. The client I'd been using for it, though, wasn't neutral at all — it was bundled with ads, occasionally prompted me to install "recommended" browser extensions I never wanted, and generally felt like software that existed to monetize me, not to serve me.</p>
<p>I'd just never questioned it, because it worked well enough and I wasn't paying anything for it directly. Except I was — in attention, in bundled junk, in trust handed over without much thought.</p>
<h2>Enter qBittorrent</h2>
<p><strong>qBittorrent</strong> is a free, open-source torrent client built specifically as a clean alternative to the ad-heavy, bundleware-laden clients that dominate search results. No ads. No bundled toolbars. No sketchy "recommended downloads" during install. Just the actual torrenting functionality, built by a community instead of a company trying to monetize the interface.</p>
<p>I installed it expecting to have to hunt for a "decline all the extra offers" checkbox somewhere in the setup, the way I'd had to with nearly every free download tool I'd used before. There wasn't one. The installer was just... the installer. Refreshing, honestly, in how little there was to be suspicious of.</p>
<h2>Using It</h2>
<p>The interface felt immediately familiar — anyone who's used a torrent client before will recognize the layout instantly: a list of active downloads, transfer speeds, seeding ratios, all clearly laid out without a single banner competing for space.</p>
<p>I went back to that Linux ISO download. Same file, same protocol, same network of peers sharing pieces of it — except this time, nothing loaded except the actual download. It finished in about the same time. The difference wasn't speed. It was everything surrounding the download that simply wasn't there anymore.</p>
<h2>What Actually Changed</h2>
<ul>
<li>No ads, no bundled software, no dark-pattern install prompts</li>
<li>Built-in search functionality for finding legitimate torrents (Linux distros, open-source releases, public domain content) without needing sketchy third-party sites</li>
<li>Bandwidth scheduling, so downloads don't hog my connection during work hours</li>
<li>Cross-platform — Windows, Mac, and Linux, all with the same clean interface</li>
<li>Regularly updated by an active open-source community, rather than a company trying to squeeze more monetization into each release</li>
</ul>
<h2>The Bigger Realization</h2>
<p>This wasn't really about torrenting specifically. It was about noticing how much "free" software I'd been using that wasn't actually free of cost — just free of a price tag, while quietly monetizing my attention, my data, or my patience through ads and bundleware instead.</p>
<p>qBittorrent didn't change what I was downloading. It changed what I had to put up with to download it.</p>
<hr />
<p><em>This is part of my "Everyday Open Source" series, where I highlight open-source tools that quietly make daily life better.</em></p>
<h2>Try It Yourself</h2>
<ul>
<li><strong>Website:</strong> <a href="https://www.qbittorrent.org">qbittorrent.org</a></li>
<li><strong>GitHub (source code):</strong> <a href="https://github.com/qbittorrent/qBittorrent">github.com/qbittorrent/qBittorrent</a></li>
<li><strong>Documentation / wiki:</strong> available on the project's GitHub wiki</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The Drawer Full of Paper I Finally Killed]]></title><description><![CDATA[I have a confession: for years, I kept a physical folder labeled "IMPORTANT DOCS" that was, in reality, a chaotic pile of receipts, warranty slips, tax forms, and at least one takeout menu that had so]]></description><link>https://adworks.hashnode.dev/the-drawer-full-of-paper-i-finally-killed</link><guid isPermaLink="true">https://adworks.hashnode.dev/the-drawer-full-of-paper-i-finally-killed</guid><category><![CDATA[Open Source]]></category><category><![CDATA[open source]]></category><category><![CDATA[opensource-daily]]></category><category><![CDATA[daily-opensource]]></category><category><![CDATA[Paperless-ngx]]></category><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Sat, 12 Sep 2026 16:45:35 GMT</pubDate><content:encoded><![CDATA[<p>I have a confession: for years, I kept a physical folder labeled "IMPORTANT DOCS" that was, in reality, a chaotic pile of receipts, warranty slips, tax forms, and at least one takeout menu that had somehow earned a permanent spot in there.</p>
<p>Every time I needed something from it — a warranty for a broken appliance, a receipt for a tax deduction, a lease agreement from three apartments ago — I'd spend twenty minutes flipping through paper, muttering, before either finding it crumpled at the bottom or admitting defeat and calling customer support instead.</p>
<p>The breaking point was a doctor's note I needed for an insurance claim. I knew I had it. I knew it was somewhere in that drawer. I never found it.</p>
<h2>The Obvious Fix, and Why It Wasn't Enough</h2>
<p>The obvious answer was "scan everything." I did — for about two weeks. I ended up with a folder on my laptop called <code>Scans</code> containing 200 files named <code>img_20230412_scan.pdf</code>, <code>img_20230412_scan(1).pdf</code>, and so on. Searchable in name only, and the names told me nothing.</p>
<p>A pile of scanned paper is still a pile. I hadn't solved the problem — I'd just changed its file format.</p>
<h2>Finding Paperless-ngx</h2>
<p>I came across <strong>Paperless-ngx</strong> while looking for something that could do more than store scans — something that could actually understand what was in them. The pitch was simple: feed it documents, and it OCRs the text, extracts dates and tags, and makes everything genuinely searchable — not by filename, but by content.</p>
<p>Open source, self-hosted, no subscription. I was cautiously optimistic, mostly because I'd been burned before by tools promising "smart" organization that turned out to be a fancy folder structure in disguise.</p>
<h2>Setting It Up</h2>
<p>I ran Paperless-ngx on my home server alongside a couple of other self-hosted tools I already had going. The setup itself was straightforward — point it at a folder, and anything dropped into that folder gets automatically processed: OCR'd, tagged, and archived.</p>
<p>I fed it a test batch: a handful of old receipts, a lease agreement, one genuinely illegible handwritten note from a plumber. Within a minute, everything showed up in the web dashboard — not as a wall of thumbnails, but as searchable, tagged documents. I searched "plumber." The handwritten note came up. I searched a dollar amount from a receipt. That came up too.</p>
<p>That's when I realized this wasn't a scanning tool. It was a search engine for my paper life.</p>
<h2>The Actual Migration</h2>
<p>Over one long weekend, I went through the drawer. All of it. Every receipt, every warranty card, every form I'd been keeping "just in case." I fed it all through a document scanner into Paperless-ngx's watch folder and let it process overnight.</p>
<p>By the next morning, years of paper clutter had become a searchable archive. I could type "warranty" and see every warranty I owned, sorted by date. I could type a company name and pull up every receipt from them, ever. Tags got applied automatically based on rules I set — anything from my bank went into "Finance," anything with "lease" in it went into "Housing."</p>
<p>I shredded the physical originals I no longer needed to legally retain. The drawer that had mocked me for years now holds spare cables.</p>
<h2>Where It Actually Helps Now</h2>
<ul>
<li><p>Tax season: every relevant receipt and form, tagged and searchable by year, no digging</p>
</li>
<li><p>Insurance claims: found that doctor's note in eight seconds instead of never</p>
</li>
<li><p>Warranty lookups: search the product name, get the receipt and warranty card instantly</p>
</li>
<li><p>New paperwork: mail gets scanned the day it arrives, filed automatically, original shredded within the week</p>
</li>
</ul>
<p>The system runs quietly in the background now. New documents get dropped in, processed, tagged, archived. I don't think about it until I need something — and then it takes seconds, not twenty minutes of drawer archaeology.</p>
<h2>What This Was Really About</h2>
<p>It wasn't really about paper. It was about how much mental overhead I'd been carrying without noticing — a low hum of "where is that document, do I still have it, did I lose it" that I'd just accepted as normal for years.</p>
<p>Paperless-ngx didn't just digitize a drawer. It removed a small, persistent source of dread I didn't realize I'd been living with.</p>
<hr />
<p><em>This is part of my "Everyday Open Source" series, where I highlight open-source tools that quietly make daily life better.</em></p>
<h2>Try It Yourself</h2>
<ul>
<li><p><strong>Website / GitHub:</strong> <a href="https://github.com/paperless-ngx/paperless-ngx">github.com/paperless-ngx/paperless-ngx</a></p>
</li>
<li><p><strong>Documentation:</strong> <a href="https://docs.paperless-ngx.com">docs.paperless-ngx.com</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The Vault I Decided Nobody Else Should Hold the Key To]]></title><description><![CDATA[A friend of mine got a notification one morning: "Your password may have appeared in a data breach." Not because she'd done anything wrong. A company she'd trusted with her password vault had a securi]]></description><link>https://adworks.hashnode.dev/the-vault-i-decided-nobody-else-should-hold-the-key-to</link><guid isPermaLink="true">https://adworks.hashnode.dev/the-vault-i-decided-nobody-else-should-hold-the-key-to</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Fri, 11 Sep 2026 17:13:23 GMT</pubDate><content:encoded><![CDATA[<p>A friend of mine got a notification one morning: "Your password may have appeared in a data breach." Not because she'd done anything wrong. A company she'd trusted with her password vault had a security incident, and suddenly every account she owned was a question mark.</p>
<p>I remember thinking, half-relieved, half-uneasy: <em>that's the app I use too.</em></p>
<p>I didn't leave that day. But the thought didn't leave either — the single most sensitive piece of software on my phone, the one holding literal keys to my email, my bank, my entire digital life, was sitting on a server I had no control over and no real visibility into.</p>
<h2>Why I Kept Putting It Off</h2>
<p>Password managers are one of those tools where "just switch" is easier said than done. Every login, every 2FA backup code, every secure note I'd built up over years — migrating that felt like performing surgery on my own digital life. So for a long time, I told myself the convenience was worth the risk.</p>
<p>Then I found out I didn't actually have to choose between the two.</p>
<h2>Enter Vaultwarden</h2>
<p>Vaultwarden is an open-source server that speaks the exact same language as Bitwarden — meaning it works with the official Bitwarden apps, browser extensions, and autofill, the same interface millions of people already use. The difference is where the vault actually lives.</p>
<p>Instead of your encrypted vault sitting on Bitwarden's cloud servers, it sits on hardware you control. Same apps. Same autofill. Same experience. Different owner.</p>
<p>I'd assumed self-hosting something like this would mean wrestling with servers, certificates, and cryptic config files for a weekend. It didn't. Vaultwarden is famously lightweight — built to run comfortably on something as small as a Raspberry Pi sitting in a closet.</p>
<h2>Setting It Up</h2>
<p>I already had a small home server running a few other self-hosted tools, so I added Vaultwarden as one more container. Within about twenty minutes — most of which was setting up a proper HTTPS certificate, not actually configuring Vaultwarden — I had a working vault server.</p>
<p>Then came the part I was dreading: migration. Bitwarden makes it almost anticlimactic. I exported my existing vault, pointed the official Bitwarden app at my new self-hosted server instead of Bitwarden's own servers, and imported everything back in. My passwords, my secure notes, my 2FA seeds — all there, all intact, just living somewhere I actually controlled.</p>
<h2>The Feeling I Didn't Expect</h2>
<p>I thought this would feel like a technical upgrade. It felt more like locking my own front door for the first time after years of leaving it open because "nothing had happened yet."</p>
<p>Every time I unlock my vault now, I know exactly where that encrypted data lives. Not "somewhere in a company's infrastructure I'll never see." A drive, in a box, in my own home, that I could physically point to if I wanted.</p>
<h2>What Changed Day to Day</h2>
<p>Honestly? Almost nothing, on the surface — and that's the best part.</p>
<ul>
<li>Autofill still works exactly the same, in every browser and app</li>
<li>I still generate strong, unique passwords for every new account</li>
<li>I still share select logins with my partner through the same sharing feature</li>
<li>2FA codes are stored right alongside the passwords, same as before</li>
</ul>
<p>The only thing that changed is who's holding the vault. And unlike the official hosted version, Vaultwarden includes premium features like TOTP generation and vault sharing for free — no subscription tier gatekeeping them.</p>
<h2>The Bigger Realization</h2>
<p>This wasn't really about saving a subscription fee, even though I did. It was about noticing how easily I'd handed over the one piece of software that, if compromised, could unravel everything else — email, banking, social accounts, all of it — without ever really deciding to.</p>
<p>Vaultwarden didn't change what a password manager does. It changed who I was trusting to do it.</p>
<hr />
<p><em>This is part of my "Everyday Open Source" series, where I highlight open-source tools that quietly make daily life better.</em></p>
<h2>Try It Yourself</h2>
<ul>
<li><strong>Website / GitHub:</strong> <a href="https://github.com/dani-garcia/vaultwarden">github.com/dani-garcia/vaultwarden</a></li>
<li><strong>Compatible official apps:</strong> <a href="https://bitwarden.com/download/">bitwarden.com/download</a></li>
<li><strong>Documentation / wiki:</strong> <a href="https://github.com/dani-garcia/vaultwarden/wiki">github.com/dani-garcia/vaultwarden/wiki</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The Notebook I Didn't Know I Needed to Take Back]]></title><description><![CDATA[I still remember the exact moment I stopped trusting my notes app.
I was scrolling through Evernote's terms of service — something I'd never actually read in the five years I'd been using it — and fou]]></description><link>https://adworks.hashnode.dev/the-notebook-i-didn-t-know-i-needed-to-take-back</link><guid isPermaLink="true">https://adworks.hashnode.dev/the-notebook-i-didn-t-know-i-needed-to-take-back</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Thu, 10 Sep 2026 05:12:53 GMT</pubDate><content:encoded><![CDATA[<p>I still remember the exact moment I stopped trusting my notes app.</p>
<p>I was scrolling through Evernote's terms of service — something I'd never actually read in the five years I'd been using it — and found a line about how they could access account content to "improve their products." My journal entries. My half-formed ideas. My private to-do lists. All of it, technically, readable by someone other than me.</p>
<p>I closed the tab, opened a new note, and typed: <em>find a new notes app.</em></p>
<h2>The Search</h2>
<p>My requirements were simple, or so I thought:</p>
<ul>
<li>My notes should be private. Actually private, not "we promise not to look" private.</li>
<li>I shouldn't need to pay a subscription just to sync between my phone and laptop.</li>
<li>I shouldn't be locked into a proprietary format that traps my years of notes if I ever want to leave.</li>
</ul>
<p>Most apps I tried failed at least one of these. Notion was beautiful but fully cloud-based, no encryption, no way to self-host meaningfully. Standard Notes looked promising but felt limited for longer-form writing. A few others wanted a monthly fee just to sync past two devices — a fee to use <em>my own notes on my own devices.</em></p>
<p>Then someone in a self-hosting forum mentioned <strong>Joplin</strong>, almost as an aside. "It's open source, encrypted, and syncs however you want." I almost scrolled past it. I'm glad I didn't.</p>
<h2>First Impressions</h2>
<p>Joplin looks unassuming — a clean, simple note-taking interface, markdown-based, nothing flashy. My first thought was, "this looks like a text editor with folders." My second thought, once I dug into the settings, was very different.</p>
<p>Every note can be end-to-end encrypted. Not "encrypted at rest on our servers" — encrypted <em>before it ever leaves your device.</em> Even if you sync through a cloud service, what lands on that server is unreadable without your key. Joplin's own maintainers, if they wanted to, couldn't read your notes.</p>
<p>And sync isn't locked to one company's infrastructure. You choose: Dropbox, OneDrive, a WebDAV server, Nextcloud, or even just a folder on your own machine. No forced subscription. No "upgrade to sync more than two devices."</p>
<h2>The Migration</h2>
<p>I exported my old notes and imported them into Joplin — plain markdown files, nothing proprietary to fight against. That alone was a small revelation. My notes weren't trapped in someone's database format anymore; they were just... files. Text I could read in any editor, forever, regardless of what happened to any app.</p>
<p>I set sync to a private Nextcloud folder I already had running, turned on encryption, and installed the mobile app. Within minutes, my phone and laptop were talking to each other — through infrastructure I controlled, in a format only I could read.</p>
<h2>Where It Actually Changed Things</h2>
<p>A few weeks in, I noticed something: I started writing more honestly.</p>
<p>That sounds small, but it wasn't. I'd been subconsciously self-censoring my own notes app for years — never quite writing that year's messy goals, that half-formed business idea, that private journal entry, because some part of me knew a company's server was involved somewhere. Once I trusted the notes were actually mine, I stopped holding back.</p>
<p>I also started using the web clipper more than I expected. Research articles, recipes, technical docs — clipped straight into an encrypted, searchable archive I own, instead of forty browser tabs I'd never revisit.</p>
<h2>What My Setup Looks Like Now</h2>
<ul>
<li>Daily journal, encrypted, synced across phone and laptop</li>
<li>A running knowledge base of technical notes, all in markdown</li>
<li>Clipped articles and references, tagged and searchable</li>
<li>Zero monthly fee — sync runs through a Nextcloud instance I already had</li>
<li>If I ever want to leave, my notes walk out the door with me as plain text files, not locked behind an export button that half-works</li>
</ul>
<h2>The Part That Stuck With Me</h2>
<p>What got me thinking wasn't really about note-taking software. It was realizing how much of my daily thinking — literal, unfiltered thoughts — had been passing through a company's servers without me ever deciding that should be the case. It wasn't a conscious choice. It was just the default I'd never questioned.</p>
<p>Joplin didn't just give me a new app. It gave me back the assumption that my own notes should, by default, be mine.</p>
<hr />
<p><em>This is part of my "Everyday Open Source" series, where I highlight open-source tools that quietly make daily life better.</em></p>
<h2>Try It Yourself</h2>
<ul>
<li><strong>Website:</strong> <a href="https://joplinapp.org">joplinapp.org</a></li>
<li><strong>GitHub (source code):</strong> <a href="https://github.com/laurent22/joplin">github.com/laurent22/joplin</a></li>
<li><strong>Documentation:</strong> <a href="https://joplinapp.org/help/">joplinapp.org/help</a></li>
<li><strong>Mobile apps:</strong> available on the App Store and Google Play</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The Day I Stopped Paying to Sync My Own Files]]></title><description><![CDATA[It was 11:47 PM on a Tuesday, and I was staring at a Google Drive storage warning.
"You've used 98% of your storage. Upgrade to Google One for more space."
I had 40GB of my own photos, notes, and proj]]></description><link>https://adworks.hashnode.dev/the-day-i-stopped-paying-to-sync-my-own-files</link><guid isPermaLink="true">https://adworks.hashnode.dev/the-day-i-stopped-paying-to-sync-my-own-files</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Wed, 09 Sep 2026 16:30:10 GMT</pubDate><content:encoded><![CDATA[<p>It was 11:47 PM on a Tuesday, and I was staring at a Google Drive storage warning.</p>
<p>"You've used 98% of your storage. Upgrade to Google One for more space."</p>
<p>I had 40GB of my own photos, notes, and project files — sitting on my own laptop, my own phone, my own home server — and a company was asking me to pay rent to move data <em>between my own devices</em>. That's when it hit me: I wasn't paying for storage. I was paying for a middleman.</p>
<p>So I went looking for a way out.</p>
<h2>The Realization</h2>
<p>Here's the thing about cloud sync services like Google Drive, Dropbox, or OneDrive: your file doesn't go directly from your laptop to your phone. It goes laptop → their server → phone. Every sync, every backup, every "your document has been updated," passes through a company's infrastructure first.</p>
<p>That's convenient. It's also a subscription, a privacy trade-off, and a single point of failure, all rolled into one.</p>
<p>I didn't need a cloud. I needed my <em>devices</em> to talk to each other.</p>
<h2>Enter Syncthing</h2>
<p>A friend mentioned an open-source tool called <strong>Syncthing</strong>, and the concept sounded almost too simple to be real: it syncs folders directly between your devices, peer-to-peer, with no central server storing your data.</p>
<p>No account. No subscription. No company sitting in the middle reading your file metadata.</p>
<p>I was skeptical. Google Drive had been my default for years — it just worked, and it was already synced across every device I owned. Every "no cloud needed" tool I'd tried before was either half-finished or required a computer science degree to configure. So I gave myself one evening to set it up before giving up on the idea entirely.</p>
<h2>Setting It Up</h2>
<p>I installed Syncthing on my laptop first — a lightweight app that opens a simple web dashboard in your browser. Then I installed it on my Android phone.</p>
<p>The moment that sold me: each device gets a unique ID, like a fingerprint. To connect two devices, you just enter one device's ID into the other. That's it. No login, no email, no "create an account to continue."</p>
<p>I shared my <code>Documents</code> folder from my laptop, added my phone as a connected device, and watched the dashboard.</p>
<p>Files started appearing on my phone. Live. In real time. No middle server. No waiting for an "upload" to finish and then a "download" to start somewhere else. Just two devices, quietly talking to each other over the network.</p>
<h2>The Moment It Clicked</h2>
<p>A few days later, I was on a train, no wifi, phone on mobile data. I edited a note in my notes app. When I got home and connected to my home network, my laptop picked up the change within seconds — because Syncthing had queued the update and pushed it the moment both devices were reachable again.</p>
<p>That's when I stopped thinking of it as "a Dropbox alternative" and started thinking of it as something more fundamental: <em>my devices had their own private network, and I was the only one on it.</em></p>
<p>I added my home server next. Now my phone photos land there automatically. Not on someone else's cloud — on a drive sitting in my closet.</p>
<h2>What Changed</h2>
<p>A few months in, here's what my setup actually looks like:</p>
<ul>
<li>My laptop and phone stay in sync automatically — documents, notes, work files</li>
<li>My phone's camera roll backs up to my home server the second I'm on wifi</li>
<li>My partner and I share a folder for household files, synced directly between our two laptops</li>
<li>Zero monthly fees. Zero storage limits beyond what my own drives can hold</li>
<li>If Syncthing's company disappeared tomorrow, my setup wouldn't break — the protocol is open, and my files are just... files, on my own drives</li>
</ul>
<p>I didn't just save money. I got my files back.</p>
<h2>Why It's Not "For Everyone" — Yet</h2>
<p>I won't pretend it's a total 1:1 Google Drive replacement. Real-time collaborative editing (like Google Docs), sharing a public link to a stranger, or accessing files from a browser on a random computer — Syncthing doesn't do those things. It's built for syncing between devices <em>you</em> control, not for broadcasting to the world.</p>
<p>But for the actual daily task most of us use cloud sync for — keeping our own laptop, phone, and home server in agreement — it does the job without asking anything in return.</p>
<h2>The Bigger Lesson</h2>
<p>What stuck with me wasn't really about file syncing. It was the pattern I noticed once I started looking: how many everyday conveniences quietly rely on data passing through someone else's server, when the two devices doing the actual talking are both sitting right there, already trusting each other.</p>
<p>Syncthing just cut out the middleman I never needed in the first place.</p>
<h2>Try It Yourself</h2>
<ul>
<li><strong>Website:</strong> <a href="https://syncthing.net">syncthing.net</a></li>
<li><strong>GitHub (source code):</strong> <a href="https://github.com/syncthing/syncthing">github.com/syncthing/syncthing</a></li>
<li><strong>Documentation:</strong> <a href="https://docs.syncthing.net">docs.syncthing.net</a></li>
<li><strong>Android app:</strong> available on <a href="https://f-droid.org/packages/com.nutomic.syncthingandroid/">F-Droid</a> and Google Play</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[VPN App vs Browser Extension vs System Settings: What's Actually Different]]></title><description><![CDATA["Just use a VPN" sounds simple until you open your VPN provider's website and see three different things to install: a desktop app, a browser extension, and instructions for setting it up manually in ]]></description><link>https://adworks.hashnode.dev/vpn-app-vs-browser-extension-vs-system-settings-what-s-actually-different</link><guid isPermaLink="true">https://adworks.hashnode.dev/vpn-app-vs-browser-extension-vs-system-settings-what-s-actually-different</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Tue, 08 Sep 2026 10:11:02 GMT</pubDate><content:encoded><![CDATA[<p>"Just use a VPN" sounds simple until you open your VPN provider's website and see three different things to install: a desktop app, a browser extension, and instructions for setting it up manually in your OS network settings. They're not the same thing, and picking the wrong one for your use case means you're either not as protected as you think, or you're using more resources than you need to.</p>
<p>Here's what each one actually does.</p>
<h2>1. The Native App (Desktop/Laptop Application)</h2>
<p>This is the full VPN client — the one with a big power button UI that most providers push you toward installing first.</p>
<p><strong>What it covers:</strong> Every app and process on your device, system-wide. Your browser, your torrent client, your email app, background updates, everything. Once it's on, all outgoing traffic from the machine routes through the VPN's encrypted tunnel.</p>
<p><strong>How it works:</strong> It typically creates a virtual network adapter at the OS level and reroutes your entire network stack through it, usually using protocols like WireGuard, OpenVPN, or IKEv2.</p>
<p><strong>When to use it:</strong> </p>
<ul>
<li>You want full-device protection, not just browser traffic.</li>
<li>You're on public Wi-Fi and want everything — not just your browsing — encrypted.</li>
<li>You use apps outside the browser that need protection too (games, torrent clients, other software).</li>
</ul>
<p><strong>Trade-off:</strong> It's heavier. Full-tunnel encryption adds more overhead than encrypting just browser traffic, and depending on the protocol and server load, you'll usually notice more of a speed hit than with a lighter extension.</p>
<h2>2. The Browser Extension</h2>
<p>This is a much lighter tool that only touches traffic going through that specific browser.</p>
<p><strong>What it covers:</strong> Only the browser it's installed in. If you open a different browser, a separate app, or a background service, none of that traffic goes through the VPN. Some "VPN" browser extensions aren't even a true VPN — many are actually just an encrypted proxy for that browser's traffic, which is a meaningfully different thing from a full VPN tunnel.</p>
<p><strong>How it works:</strong> It reroutes the browser's HTTP/HTTPS requests through a remote server, often via a proxy protocol rather than a full network-level tunnel.</p>
<p><strong>When to use it:</strong> </p>
<ul>
<li>Quick geo-unblocking for browser-based streaming or content.</li>
<li>Lightweight browsing privacy without wanting to reroute your entire device.</li>
<li>You don't want the performance overhead of encrypting non-browser traffic you don't care about.</li>
</ul>
<p><strong>Trade-off:</strong> Weaker protection. If you're using it and think your whole device is now anonymized, it isn't. Apps outside the browser, DNS requests made by the OS, and other background traffic aren't covered.</p>
<h2>3. Built-In OS/System VPN Settings</h2>
<p>Every major OS — Windows, macOS, iOS, Android — has a native VPN configuration screen buried in system settings. This isn't a separate product; it's the OS providing a slot to plug a VPN connection into using standard protocols.</p>
<p><strong>What it covers:</strong> System-wide, just like the native app — but without the provider's extra software, UI, or built-in features like a kill switch, split tunneling controls, or ad blocking.</p>
<p><strong>How it works:</strong> You manually enter server details, protocol, and credentials your VPN provider gives you (or an "on-demand" config file), and the OS handles the connection itself using its built-in VPN protocol support, without needing the vendor's app running in the background.</p>
<p><strong>When to use it:</strong></p>
<ul>
<li>Devices where you can't or don't want to install a third-party app (some work devices, routers, smart TVs, or lightweight system installs).</li>
<li>You want a lighter-footprint connection without the extra app running in the background.</li>
<li>Setting up a VPN on your home router, so every device on the network is covered without installing anything on each one individually.</li>
</ul>
<p><strong>Trade-off:</strong> No extra features. No kill switch, no split-tunneling UI, no automatic server selection — you get the raw encrypted tunnel and nothing else. If your connection drops, most system-level setups won't automatically block traffic the way a dedicated app's kill switch does.</p>
<h2>Quick Comparison</h2>
<table>
<thead>
<tr>
<th></th>
<th>Covers</th>
<th>Best for</th>
<th>Missing features</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Native App</strong></td>
<td>Entire device</td>
<td>Full protection, public Wi-Fi, non-browser apps</td>
<td>Heavier resource use</td>
</tr>
<tr>
<td><strong>Browser Extension</strong></td>
<td>Just that browser</td>
<td>Quick geo-unblocking, lightweight browsing</td>
<td>No device-wide protection, often proxy not true VPN</td>
</tr>
<tr>
<td><strong>System Settings</strong></td>
<td>Entire device</td>
<td>Devices without app support, routers</td>
<td>No kill switch, no extras, manual setup</td>
</tr>
</tbody></table>
<h2>Which One Should You Actually Use?</h2>
<p>If you want real protection — banking on public Wi-Fi, hiding your traffic from your ISP, anonymizing everything you do — use the <strong>native app</strong>. It's built for that job and includes safety nets like a kill switch that the other two don't have.</p>
<p>If you just want to watch a show that's geo-blocked or don't want your browsing history tied to your IP for casual browsing, the <strong>extension</strong> is lighter and good enough for that narrow job — just don't mistake it for full protection.</p>
<p>Use the <strong>system-level setup</strong> when you're configuring a device that can't run the provider's app, or when you want one VPN connection to cover an entire router's worth of devices at once.</p>
<p>They're not competing options — they're different tools solving different-sized problems. The mistake is assuming any one of them does what the others do.</p>
]]></content:encoded></item><item><title><![CDATA[Island: The Open Source Fix for Dual Apps, Work Profiles, and App Isolation on Android

]]></title><description><![CDATA[Some phones ship with a built-in "Dual Apps" or "App Twin" feature that lets you clone WhatsApp, Instagram, or your work email into a second instance. Great — if your manufacturer bothered to include ]]></description><link>https://adworks.hashnode.dev/island-the-open-source-fix-for-dual-apps-work-profiles-and-app-isolation-on-android</link><guid isPermaLink="true">https://adworks.hashnode.dev/island-the-open-source-fix-for-dual-apps-work-profiles-and-app-isolation-on-android</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Tue, 08 Sep 2026 00:53:28 GMT</pubDate><content:encoded><![CDATA[<p>Some phones ship with a built-in "Dual Apps" or "App Twin" feature that lets you clone WhatsApp, Instagram, or your work email into a second instance. Great — if your manufacturer bothered to include it. If you're on a phone that doesn't, you're stuck juggling accounts, logging in and out, or installing sketchy clone apps from random APK sites.</p>
<p>I ran into exactly this problem trying to keep a work profile separate from my personal one on a phone that had no native way to do it. That's what led me to Island, and this is a breakdown of what it actually does, where it's genuinely useful, and where it isn't.</p>
<h2>What Island Actually Is</h2>
<p>Island is a free, open-source Android app built by developer Oasis Feng. Instead of faking a clone the way some third-party "parallel space" apps do, it lets you clone an app for parallel running, freeze an app to block its background activity, and hide an app entirely. It does this by creating a genuine <strong>work profile</strong> on your device — the same underlying Android infrastructure Google built for enterprise device management, repurposed here for personal use.</p>
<p>Because it rides on Android's real managed-profile system rather than a hack, Island creates a separate space for apps within the work profile, keeping them isolated from your main profile and its data. Anything you put inside that space — a second WhatsApp, a work Gmail, a banking app you don't fully trust — runs in its own sandbox with its own storage and permissions.</p>
<h2>Why This Matters on "Unsupported" Phones</h2>
<p>If your device doesn't have a native dual-app feature, Island effectively builds one for you using standard Android APIs, which is why it works across a much wider range of devices than manufacturer-specific tools. There's one important caveat worth knowing before you install it: if your device already has "Dual Apps," "App Twin," or a similar feature, it may be built on the same underlying infrastructure as Island, which causes a conflict — meaning you can't run both at once. In other words, Island isn't meant to stack on top of a native dual-app feature; it's meant to replace the need for one.</p>
<h2>What You Can Actually Do With It</h2>
<ul>
<li><strong>Run two accounts of the same app</strong> — a personal and work WhatsApp, two Gmail logins, dual gaming accounts — without needing separate physical devices.</li>
<li><strong>Freeze apps</strong> so they stop running in the background entirely, which is handy for battery life or apps you rarely use but don't want to uninstall.</li>
<li><strong>Hide apps</strong> from your main launcher and app drawer for privacy.</li>
<li><strong>Split VPN usage</strong> — you can run a VPN only on one side, or run different VPNs on each side, which is genuinely useful if you want your work profile routed differently than your personal one.</li>
<li><strong>Keep personal data boundaries intact</strong> — apps inside the Island space can't see your contacts, call history, or photos from outside it.</li>
</ul>
<h2>The Trade-offs</h2>
<p>Island isn't magic, and it's worth going in with realistic expectations:</p>
<ul>
<li><strong>Setup has a learning curve.</strong> You're creating a work profile, which is a heavier concept than just tapping "clone app" in a manufacturer's settings menu.</li>
<li><strong>Some apps behave differently inside it.</strong> A handful of apps detect they're in a managed/work profile and restrict certain features.</li>
<li><strong>Extra battery and resource use.</strong> Running a second instance of an app means the OS is genuinely managing two live profiles, not a lightweight fake.</li>
<li><strong>Uninstalling isn't a simple drag-to-trash.</strong> You need to destroy the Island space from within the app first, or manually remove the work profile from your system settings if you've already deleted the app.</li>
</ul>
<h2>The Personal-vs-Work Case</h2>
<p>One of the most practical reasons to use Island has nothing to do with running two accounts of the same app — it's keeping your personal and work life on separate profiles entirely.</p>
<p>If you use one phone for both, putting your work apps (email, Slack, VPN client, whatever your employer requires) inside the Island space means:</p>
<ul>
<li>Your employer's mobile device management, if any, stays contained to that profile — it can't see your personal photos, messages, or browsing.</li>
<li>You can wipe the entire work profile in one tap if you leave the job, without touching your personal data.</li>
<li>Notifications, storage, and background activity for work apps stay separate from your personal phone experience, so work doesn't quietly drain your personal battery or clutter your main app drawer.</li>
<li>If your company ever asks to install a management profile on your device, Island's work-profile approach is the same underlying mechanism, so you're not fighting the system — you're using it on your own terms before someone else does it for you.</li>
</ul>
<p>It's less flashy than running two Instagram accounts, but for a lot of people, this is the actual use case that makes Island worth installing in the first place.</p>
<h2>Who Should Actually Use It</h2>
<p>If you're someone who needs two accounts on one app — a side hustle Instagram, a second WhatsApp number, separating work and personal messaging — and your phone manufacturer never gave you that option, Island is one of the few tools that solves this properly instead of with a shady clone APK. It's open source, it's actively maintained, and it uses Android's own framework instead of fighting against it.</p>
<p>It's not the flashiest app on your home screen. But it's quietly one of the more useful ones if you've ever needed "just one more" instance of something your phone wasn't built to give you.</p>
<h2>Heads Up: Island's Managed Mainland Mode (Beta) — Proceed With Caution</h2>
<p>If you go digging through Island's settings, you might come across a feature called <strong>Managed Mainland</strong>. It's currently in closed beta, and it's worth knowing what it does before you touch it.</p>
<p>A few things to keep in mind:</p>
<ul>
<li><strong>Order matters.</strong> Once Managed Mainland is activated, you can no longer create a new Island space afterward. If you want to use both, set up Island first.</li>
<li><strong>Older devices lose cloud backup.</strong> On Android versions prior to 7.1, app backup (like Google Drive backup for app data) stops working entirely under this mode. Android 7.1 and newer are unaffected.</li>
<li><strong>It's manual setup only</strong> — there's no streamlined in-app flow for it yet.</li>
</ul>
<p>And the developer's own disclaimer is blunt about the risk:</p>
<blockquote>
<p>This beta version may be dangerous on some Android devices, it may cause boot-loop and even brick your device. The purpose of closed beta exclusive for advanced users is to widely test and improve the device compatibility. Don't install it on your daily device and remember to BACKUP FIRST.</p>
</blockquote>
<p>In plain terms: this isn't a feature for casual users, and it's not something to try on the phone you actually depend on. If you're curious and want to help test device compatibility, use a spare device and back up first. For everyone else, the standard Island setup covered above is the stable, safe path — Managed Mainland is optional territory for advanced users only.</p>
<h2>Get It Here</h2>
<p>Island is available on the <a href="https://play.google.com/store/apps/details?id=com.oasisfeng.island">Google Play Store</a>, and since it's open source, you can also find the source code and setup docs on the developer's official site at <a href="https://island.oasisfeng.com">island.oasisfeng.com</a>. If you'd rather avoid Google Play Services entirely, there's a fully FOSS fork called Insular available through F-Droid with the same core functionality.</p>
<p>If your device already has a native "Dual Apps" feature, check the FAQ on the official site first — it explains the conflict mentioned earlier and how to work around it.</p>
]]></content:encoded></item><item><title><![CDATA[The Night I Stopped Scrolling GitHub and Actually Found My First Open Source Project]]></title><description><![CDATA[Three months ago, I had 47 browser tabs open. All GitHub. All repositories I'd starred "to contribute to later." I had never opened a single pull request.
Sound familiar?
The Problem Nobody Warns You ]]></description><link>https://adworks.hashnode.dev/the-night-i-stopped-scrolling-github-and-actually-found-my-first-open-source-project</link><guid isPermaLink="true">https://adworks.hashnode.dev/the-night-i-stopped-scrolling-github-and-actually-found-my-first-open-source-project</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Mon, 07 Sep 2026 08:35:35 GMT</pubDate><content:encoded><![CDATA[<p>Three months ago, I had 47 browser tabs open. All GitHub. All repositories I'd starred "to contribute to later." I had never opened a single pull request.</p>
<p>Sound familiar?</p>
<h2>The Problem Nobody Warns You About</h2>
<p>Everyone tells you "just contribute to open source" like it's as simple as walking into a library and picking a book. Nobody mentions that GitHub has over 400 million repositories, that half of them are abandoned side projects from 2019, and that finding a project that's both <em>alive</em> and <em>welcoming</em> feels like searching for a specific grain of sand on a beach.</p>
<p>I spent weeks doing what I now call "tourist browsing" — clicking into trending repos, admiring the code, feeling intimidated, and closing the tab. I wasn't finding projects. I was window shopping.</p>
<p>The breaking point came on a Tuesday night when I opened yet another "awesome" repo with 30k stars, found an issue that looked approachable, and discovered it had 40 comments of people asking if it was already taken. Nobody had touched it in eight months.</p>
<h2>The Small Trick That Changed Everything</h2>
<p>A friend who'd been contributing to open source for years watched me doing this and asked one question: "Why are you searching for <em>projects</em>? Search for <em>issues</em>."</p>
<p>It sounds obvious in hindsight, but it rewired how I approached the whole thing. Instead of asking "what project should I join," I started asking "what specific, well-described problem can I solve this weekend?"</p>
<p>I typed this into GitHub's search bar:</p>
<pre><code>label:"good first issue" language:python state:open
</code></pre>
<p>Instead of 400 million repositories, I had a filtered list of <em>actual tasks</em>, written by maintainers who wanted newcomers specifically. The whole frame shifted — I wasn't auditioning for a project, I was picking a task off a job board.</p>
<h2>The Project That Actually Stuck</h2>
<p>The issue I picked wasn't glamorous. It was a documentation fix in a mid-sized CLI tool — maybe 2,000 stars, nothing trending. But three things were true about it:</p>
<ul>
<li>The maintainer had replied to an issue two days earlier.</li>
<li>There was a <code>CONTRIBUTING.md</code> that actually explained the setup, not just a wall of boilerplate.</li>
<li>The last ten pull requests had all been merged within a week.</li>
</ul>
<p>None of that is exciting to read about. But it's the entire difference between a contribution that goes somewhere and one that dies in review purgatory.</p>
<p>I submitted the fix. The maintainer merged it in two days and left a comment: "Thanks, want to try the next one?" That single sentence did more for my motivation than any "awesome open source" list ever had.</p>
<h2>The Real Lesson</h2>
<p>I'm not going to pretend that one merged PR turned me into a seasoned open source contributor overnight. But it did change how I think about the search itself.</p>
<p>The lesson wasn't "find the right project." It was: stop treating GitHub like a museum and start treating it like a job board. Filter by labels. Check who actually responds. Pick something unglamorous with a maintainer who replies. The rest takes care of itself, one issue at a time.</p>
<hr />
<p><em>If you're stuck in the tourist-browsing phase right now — go run that label search. It's a smaller trick than it sounds, and it's the only reason I stopped collecting tabs and started collecting merged PRs.</em></p>
]]></content:encoded></item><item><title><![CDATA[I Committed My API Keys to GitHub. Here's What I Learned Building a Bot That Hides Secrets.]]></title><description><![CDATA[Building an Image Steganography Bot with Telegram and Azure Functions
What if a normal-looking image could carry a private message without adding another file or interface to the conversation?
That qu]]></description><link>https://adworks.hashnode.dev/i-committed-my-api-keys-to-github-here-s-what-i-learned-building-a-bot-that-hides-secrets</link><guid isPermaLink="true">https://adworks.hashnode.dev/i-committed-my-api-keys-to-github-here-s-what-i-learned-building-a-bot-that-hides-secrets</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Sun, 06 Sep 2026 15:00:11 GMT</pubDate><content:encoded><![CDATA[<h1>Building an Image Steganography Bot with Telegram and Azure Functions</h1>
<p>What if a normal-looking image could carry a private message without adding another file or interface to the conversation?</p>
<p>That question led me to build <strong>ImgSteganoBot</strong>, a Telegram bot that encodes text into images and later decodes it with a security code. The project combines Telegram's conversational interface with Azure Functions, Azure Blob Storage, and image-processing logic based on histogram shifting.</p>
<p><em>(GitHub: <a href="https://github.com/deebadithya/ImageStegnoBot">deebadithya/ImageStegnoBot</a>)</em></p>
<hr />
<h2>The user experience</h2>
<p>The bot is designed around a short conversation:</p>
<ol>
<li>Send <code>/encode</code> and provide the text to hide.</li>
<li>Upload an image as a document so Telegram preserves the original bytes.</li>
<li>Receive an encoded image and a generated security code.</li>
<li>Send <code>/decode</code>, provide the code, and upload the encoded image.</li>
<li>The bot extracts and returns the hidden message.</li>
</ol>
<p>The command flow keeps the interaction approachable. Users do not need to learn a command-line tool or manage a separate web application.</p>
<h2>How it works</h2>
<p>The Azure Function receives Telegram webhook updates over HTTP and passes them to the bot handlers. During encoding, the image-processing module converts the message into binary data and embeds it in the image. During decoding, the same process reads the hidden data and reconstructs the original text.</p>
<pre><code>Telegram → Azure Function (webhook + handlers)
                   │
        ┌──────────┴──────────┐
        ▼                     ▼
Image processing        Blob Storage
(encode / decode)       (temp image files)
</code></pre>
<p>Generated identifiers give output files unique names, while Azure Blob Storage provides a place to upload and retrieve image data when the workflow needs persistent storage.</p>
<p>One practical detail matters here: images sent as compressed photos can lose pixel information. The current bot therefore asks users to send images as documents, which helps preserve the data required for reliable decoding. Direct photo handling is an area for future improvement.</p>
<h2>Security lessons</h2>
<p>The first version stored the Telegram token, webhook authorization code, and Azure Storage credentials directly in source code. That is convenient during a prototype, but dangerous as soon as the project is shared or pushed to a repository.</p>
<pre><code class="language-env"># .env.example
TELEGRAM_BOT_TOKEN=
WEBHOOK_AUTH_CODE=
AZURE_STORAGE_CONNECTION_STRING=
</code></pre>
<p>The project now reads these values from environment variables instead. A checked-in <code>.env.example</code> documents the required names without containing live credentials.</p>
<p>In a real deployment, secrets should be configured through Azure Function App settings or a managed secret store, and any credentials that were previously exposed should be rotated immediately.</p>
<h2>What I learned</h2>
<p>This project taught me that a useful security tool is as much about workflow design as algorithms. Preserving image bytes, guiding users through each step, cleaning up temporary chat messages, and handling deployment configuration all affect whether the core technique works in practice.</p>
<p>The next steps are to:</p>
<ul>
<li>Improve direct photo support without losing pixel data</li>
<li>Isolate conversation state per Telegram user</li>
<li>Add automated tests for round-trip encoding and decoding</li>
<li>Replace shared storage credentials with managed identity where possible</li>
</ul>
<p>ImgSteganoBot is a small project, but it sits at an interesting intersection of messaging, cloud functions, and information hiding. It has been a practical way to explore how an image-processing idea becomes a usable service.</p>
<hr />
<p><em>Code: <a href="https://github.com/deebadithya/ImageStegnoBot">github.com/deebadithya/ImageStegnoBot</a></em></p>
]]></content:encoded></item><item><title><![CDATA[TrackSto: A Full-Stack Stock Watchlist and Monitoring App with React and Django]]></title><description><![CDATA[Building TrackSto: A Full-Stack Stock Watchlist and Monitoring App with React and Django

A practical case study of building a personal stock-monitoring dashboard with watchlists, intraday charts, JWT]]></description><link>https://adworks.hashnode.dev/tracksto-a-full-stack-stock-watchlist-and-monitoring-app-with-react-and-django</link><guid isPermaLink="true">https://adworks.hashnode.dev/tracksto-a-full-stack-stock-watchlist-and-monitoring-app-with-react-and-django</guid><dc:creator><![CDATA[A.D Adithya]]></dc:creator><pubDate>Sun, 06 Sep 2026 11:58:45 GMT</pubDate><content:encoded><![CDATA[<h1>Building TrackSto: A Full-Stack Stock Watchlist and Monitoring App with React and Django</h1>
<blockquote>
<p>A practical case study of building a personal stock-monitoring dashboard with watchlists, intraday charts, JWT authentication, and a server-side data layer.</p>
</blockquote>
<p><strong>Published:</strong> May-June 2024</p>
<p>Stock dashboards often begin with a simple question: “What are the assets I care about doing right now?” The difficult part is turning that question into a useful product. A good dashboard needs more than an API call. It needs authentication, user-specific state, sensible caching, clear visualisation, and an interface that makes repeated monitoring easy.</p>
<p>That was the motivation behind <strong>TrackSto</strong>, a full-stack stock tracking and monitoring web application. Users can register, sign in, search a catalogue of symbols, add stocks to a personal watchlist, and inspect intraday values through an interactive line chart.</p>
<p>This article explains how the project is structured, how data moves through the system, and what I learned while building it.</p>
<h2>What TrackSto does</h2>
<p>TrackSto focuses on a small but complete monitoring workflow:</p>
<ul>
<li>A user creates an account and logs in.</li>
<li>The frontend stores the JWT access and refresh tokens locally.</li>
<li>Authenticated requests automatically include the access token.</li>
<li>The user searches a local symbol catalogue by ticker.</li>
<li>A selected symbol is saved to that user's watchlist.</li>
<li>The backend retrieves intraday data from Alpha Vantage when needed.</li>
<li>The response is stored in a local daily cache file to avoid repeated requests.</li>
<li>The dashboard displays the latest values in a table.</li>
<li>The user selects a watchlist symbol and chooses Open, High, Low, Close, or Volume for the chart.</li>
</ul>
<p>The result is a focused monitoring tool rather than a trading platform. It is designed to make a user's preferred symbols easy to revisit and compare.</p>
<h2>Technology choices</h2>
<h3>Frontend</h3>
<ul>
<li>React 18</li>
<li>React Router for navigation and protected routes</li>
<li>Material UI for layout, controls, authentication screens, and the data grid</li>
<li>Nivo Line for the interactive time-series chart</li>
<li>Axios for API communication</li>
<li><code>jwt-decode</code> for checking access-token expiry in the browser</li>
</ul>
<h3>Backend</h3>
<ul>
<li>Django 5</li>
<li>Django REST Framework</li>
<li>SQLite for user and watchlist persistence</li>
<li>SimpleJWT for access and refresh tokens</li>
<li><code>django-cors-headers</code> for controlled frontend-to-backend requests</li>
<li><code>requests</code> for Alpha Vantage calls</li>
</ul>
<h3>Data provider</h3>
<p>TrackSto uses Alpha Vantage's <code>TIME_SERIES_INTRADAY</code> endpoint with a five-minute interval. The frontend receives the provider response through Django instead of calling Alpha Vantage directly. This keeps the external API integration in one place and prevents the browser from becoming responsible for provider credentials and request policy.</p>
<h2>High-level architecture</h2>
<p>The application is split into two independently runnable projects:</p>
<pre><code class="language-text">TrackSto/
├── backend/
│   ├── manage.py
│   ├── stocktrack/
│   │   ├── settings.py
│   │   └── urls.py
│   └── stockapp/
│       ├── models.py
│       ├── serializers.py
│       ├── views.py
│       └── urls.py
└── frontened/
    ├── package.json
    └── src/
        ├── api.js
        ├── App.js
        ├── components/
        ├── global/
        └── pages/
</code></pre>
<p>The browser talks to Django through Axios. Django owns authentication, user-specific watchlists, and the Alpha Vantage integration. The database stores the relationship between a user and a symbol, while the current implementation stores fetched market responses in <code>temp_storage.json</code> as a date-based cache.</p>
<h2>Designing the watchlist model</h2>
<p>The core database model is intentionally small:</p>
<pre><code class="language-python">class WatchList(models.Model):
    symbol = models.CharField(max_length=10)
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="watchlist",
    )
</code></pre>
<p>A watchlist entry belongs to a Django user. That ownership relationship is important: the same symbol can appear in many users' watchlists, but each user should only receive their own entries.</p>
<p>The list endpoint filters by the authenticated request user:</p>
<pre><code class="language-python">def get_queryset(self):
    return WatchList.objects.filter(author=self.request.user)
</code></pre>
<p>When a new item is created, the backend assigns the author from the token rather than trusting a user-supplied request field:</p>
<pre><code class="language-python">def perform_create(self, serializer):
    serializer.save(author=self.request.user)
</code></pre>
<p>This is a useful general rule for multi-user applications: identity and ownership should come from the authenticated request context whenever possible.</p>
<h2>Authentication with JWT</h2>
<p>The backend exposes three important authentication routes:</p>
<pre><code class="language-text">POST /api/user/register/
POST /api/token/
POST /api/token/refresh/
</code></pre>
<p>The registration serializer uses Django's <code>create_user</code> method, so passwords are hashed by Django instead of being saved as plain text.</p>
<p>On the frontend, the Axios instance adds the access token to every outgoing request:</p>
<pre><code class="language-javascript">api.interceptors.request.use((config) =&gt; {
  const token = localStorage.getItem(ACCESS_TOKEN);

  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }

  return config;
});
</code></pre>
<p>The <code>ProtectedRoute</code> component decodes the token, checks its expiry, and attempts to refresh it before allowing the dashboard to render. This keeps unauthenticated users away from the main application route while still supporting short-lived access tokens.</p>
<h2>Keeping Alpha Vantage behind the backend</h2>
<p>The dashboard does not call Alpha Vantage directly. Instead, the watchlist API fetches data for the user's symbols and converts it into a response keyed by symbol.</p>
<p>The provider request uses the intraday endpoint:</p>
<pre><code class="language-python">url = (
    "https://www.alphavantage.co/query"
    f"?function=TIME_SERIES_INTRADAY"
    f"&amp;symbol={symbol}"
    f"&amp;interval=5min"
    f"&amp;apikey={api_key}"
)
</code></pre>
<p>The response contains a time-series object whose fields look like this:</p>
<pre><code class="language-json">{
  "2024-06-20 15:55:00": {
    "1. open": "184.20",
    "2. high": "184.55",
    "3. low": "184.10",
    "4. close": "184.40",
    "5. volume": "123456"
  }
}
</code></pre>
<p>The backend checks <code>temp_storage.json</code> before making a new request. If the symbol was already fetched on the current date, the stored response is returned. If the cached date is older, the application requests fresh data and replaces the cached entry.</p>
<p>This approach reduces duplicate requests during a development session and helps avoid Alpha Vantage rate limits. It is a simple daily cache rather than a full market-data storage system, but it is enough for the first version of the product.</p>
<h2>Building the dashboard</h2>
<p>The dashboard combines three primary areas:</p>
<ol>
<li>A top bar with the TrackSto logo, symbol autocomplete, and the account menu.</li>
<li>A chart area with symbol selection and a field selector.</li>
<li>A Material UI data grid containing the user's watchlist and recent values.</li>
</ol>
<p>The autocomplete uses a bundled symbol catalogue containing ticker symbols and descriptions. Selecting an item triggers a POST request to the watchlist endpoint. Duplicate symbols are ignored on the client before the request is sent.</p>
<p>The dashboard maps friendly labels to Alpha Vantage's response keys:</p>
<pre><code class="language-javascript">const columns = {
  Open: "1. open",
  Close: "4. close",
  Volume: "5. volume",
  Low: "3. low",
  High: "2. high",
};
</code></pre>
<p>That mapping keeps the interface readable while still allowing the chart component to consume the provider's original field names.</p>
<h2>Transforming data for Nivo</h2>
<p>Nivo expects a series with <code>id</code> and <code>data</code> fields, while Alpha Vantage returns a dictionary keyed by timestamp. The chart component bridges those two formats:</p>
<pre><code class="language-javascript">const transformData = (chartMeta, key) =&gt; {
  const timeSeries = chartMeta["Time Series (5min)"];
  const data = [];

  for (const timestamp in timeSeries) {
    data.push({
      x: new Date(timestamp),
      y: parseFloat(timeSeries[timestamp][key]),
    });
  }

  return [{ id: key, data: data.reverse() }];
};
</code></pre>
<p>The chart uses a time scale, formats the horizontal axis as hours and minutes, enables a mesh for easier hover interactions, and displays points along the line. Reversing the entries gives the chart chronological order even though the API response is returned newest-first.</p>
<p>The selected field changes the <code>key</code> passed into the chart. That makes one chart component reusable for Open, High, Low, Close, and Volume instead of creating a separate component for each metric.</p>
<h2>The request flow</h2>
<p>When a user opens the dashboard, the main flow looks like this:</p>
<pre><code class="language-text">React Dashboard
    |
    | GET /api/watchlist/
    v
Django WatchListCreate
    |
    | Filter WatchList rows by request.user
    v
For each symbol:
    |-- return today's temp_storage.json entry
    |-- or request Alpha Vantage and cache the response
    v
Processed JSON keyed by ticker
    |
    v
DataGrid + Nivo Line Chart
</code></pre>
<p>When a user removes a symbol, the frontend sends a DELETE request using the watchlist row's database ID. After a successful response, it reloads the watchlist so the grid and chart selector stay synchronized with the server.</p>
<h2>Running the project locally</h2>
<h3>Backend</h3>
<p>From the <code>backend</code> directory, create and activate a virtual environment, install Django, Django REST Framework, SimpleJWT, CORS headers, Requests, and dotenv, then run migrations:</p>
<pre><code class="language-bash">python -m venv .venv
.venv\Scripts\activate
pip install django djangorestframework djangorestframework-simplejwt django-cors-headers requests python-dotenv
python manage.py migrate
python manage.py runserver
</code></pre>
<p>The API runs at <code>http://127.0.0.1:8000</code> by default.</p>
<h3>Frontend</h3>
<p>From the <code>frontened</code> directory:</p>
<pre><code class="language-bash">npm install
npm start
</code></pre>
<p>The React development server runs at <code>http://localhost:3000</code>. The frontend API base URL should point to the Django server through the project's environment configuration.</p>
<h2>What I learned</h2>
<h3>1. A small data model can support a useful product</h3>
<p>The watchlist only needs a symbol and an owner, but that simple relationship enables registration, personalized state, filtering, and deletion. Starting with a focused model made it easier to build the complete workflow before adding more market features.</p>
<h3>2. Caching belongs near the external integration</h3>
<p>The frontend should not need to know whether data came from Alpha Vantage or a local cache. Keeping that decision in the backend made the UI simpler and gave the server one place to control provider traffic.</p>
<h3>3. Data visualisation is mostly data preparation</h3>
<p>The chart library was not the hardest part. The important work was converting timestamp-keyed API data into the series format the chart expected, parsing values as numbers, and giving users a meaningful time axis.</p>
<h3>4. Authentication is a system concern</h3>
<p>Protecting a page is not enough. The API must filter data by the authenticated user, the serializer must avoid accepting ownership from the client, and the frontend must handle token expiry. The watchlist endpoint demonstrates how these layers work together.</p>
<h2>Production improvements</h2>
<p>The project is a functional learning application, but I would make several changes before deploying it publicly:</p>
<ul>
<li>Move the Alpha Vantage key into an environment variable and rotate the exposed development key.</li>
<li>Replace the JSON cache with a database-backed cache or Redis so multiple processes can share it safely.</li>
<li>Add a uniqueness constraint on <code>(author, symbol)</code> to prevent duplicate rows under concurrent requests.</li>
<li>Use <code>IsAuthenticated</code> for the delete endpoint instead of allowing anonymous access at the view level.</li>
<li>Fix the refresh-token request to send the actual token value from local storage.</li>
<li>Store tokens in a more defensive browser strategy, such as secure, HttpOnly cookies where the deployment architecture allows it.</li>
<li>Add validation for malformed symbols and provider error responses.</li>
<li>Add automated tests for registration, token access, watchlist ownership, duplicate symbols, deletion, and cache expiry.</li>
<li>Move Alpha Vantage requests out of the list serializer path if the application grows, using a scheduled job or background worker.</li>
</ul>
<p>These improvements are not just polish. They address credential safety, concurrency, authorization, resilience, and maintainability.</p>
<h2>Final thoughts</h2>
<p>TrackSto started as a stock information dashboard and became a compact full-stack exercise in user-specific data, authentication, API integration, caching, and visualisation. React and Material UI made it possible to build the interface quickly, while Django REST Framework provided a clear place to enforce ownership and keep the external stock provider behind the server.</p>
<p>The most valuable part of the project was learning how these concerns connect. A chart is only useful when the data is current. Current data is only practical when requests are controlled. A watchlist is only personal when the API enforces ownership. And all of it needs an interface that makes the next decision obvious.</p>
<p>That combination is what turned TrackSto from a stock API experiment into a usable monitoring application.</p>
<hr />
<p><strong>Tags:</strong> <code>react</code>, <code>django</code>, <code>python</code>, <code>web-development</code>, <code>javascript</code></p>
<p><strong>Suggested Hashnode excerpt:</strong></p>
<blockquote>
<p>Learn how I built TrackSto, a full-stack stock monitoring dashboard with React, Material UI, Django REST Framework, JWT authentication, Alpha Vantage intraday data, personal watchlists, daily caching, and interactive Nivo charts.</p>
</blockquote>
]]></content:encoded></item></channel></rss>