<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Anh&#39;s Tech Den</title>
    <link>https://blog.ampho.fr/</link>
    <description>Recent content on Anh&#39;s Tech Den</description>
    <generator>Hugo -- 0.149.0</generator>
    <language>en</language>
    <copyright>2025 Anh Minh PHO</copyright>
    <lastBuildDate>Thu, 26 Mar 2026 13:15:25 +0100</lastBuildDate>
    <atom:link href="https://blog.ampho.fr/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>A few ideas to move away from Big Tech</title>
      <link>https://blog.ampho.fr/posts/move-away-from-big-tech/</link>
      <pubDate>Fri, 06 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/move-away-from-big-tech/</guid>
      <description>A self-host a day keeps Big Tech away.</description>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Given what&rsquo;s happening in the world right now, I really want to minimize my reliance on US-based services and Big Tech (&ldquo;Les GAFAM&rdquo; for French readers), even more so than a few months ago. Here&rsquo;s a quick summary of what I use and do in an effort to become independent from those corporations. Almost everything mentioned is FOSS (Free and Open-Source Software).</p>
<p>This is mainly addressed to tech-savvy people, but it can give you some ideas to get started if you&rsquo;re not one (yet) :). Each solution will be attributed a &ldquo;pain level&rdquo;, which rates how painful the setup was for me, with my knowledge at the time. It&rsquo;s therefore entirely subjective and is mainly just for fun. Feeling interested but lost? Feel free to reach out! See <a href="https://blog.ampho.fr/about/">about page</a>.</p>
<p>And if you&rsquo;re asking why do all this:</p>
<ul>
<li>I want to have control over my data, I do not want my information sold by data brokers</li>
<li>I think privacy is a fundamental right and that more people should care</li>
<li>I want to own things instead of subscribing to services, which are enshitiffied update after update, quarterly result after quarterly result - there are paid services with ads now, are we for real?[insert many more?] But I guess line must go up because shareholder angry if line go down</li>
<li>I do not want my daily activities to support evil companies a.k.a. Big Tech (ad revenue, &ldquo;&ldquo;&ldquo;AI&rdquo;&rdquo;&rdquo; training on my data, etc.)</li>
<li>It&rsquo;s part of my hobbies - no I&rsquo;m not a masochist I promise</li>
<li>Other reasons I&rsquo;ve forgotten at the time of writing or that I&rsquo;m too lazy to write down</li>
</ul>
<h2 id="self-hosting">Self-hosting</h2>
<p>It doesn&rsquo;t get better than that, if you have the means and technical ability. I have a dedicated server at home that hosts most of the services I&rsquo;m going to list. It&rsquo;s a repurposed mini PC, but a Raspberry Pi or an old desktop or laptop is more than enough to get started. You could also rent a VPS if you have the money and don&rsquo;t want to trouble yourself with the hardware (but in that case you would be using someone else&rsquo;s computer, that&rsquo;s what &ldquo;cloud&rdquo; really is&hellip;).</p>
<h3 id="first-and-foremost---a-domain-name">First and foremost - a domain name</h3>
<p>I recommend getting a domain name. It makes self-hosting much easier and prettier: no need to deal with a potentially changing IP all the time (take advantage of DynDNS if your ISP does not provide static IPs and if your router supports it). I purchased mine at OVHcloud, formerly OVH, a French &ldquo;cloud&rdquo; company. I find the price fair, and the new online dashboard is alright.</p>
<h3 id="nextcloud---replaces-most-of-the-google-suite">Nextcloud - replaces most of the Google suite</h3>
<p>Pain level: 2/5 - installation instructions are rather clear, and I had little experience with self-hosting at the time.</p>
<p>The features I use are:</p>
<ul>
<li>Cloud storage
<ul>
<li>Files</li>
<li>Notes</li>
<li>Tasks and reminders</li>
</ul>
</li>
<li>CalDAV and CardDAV server for <strong>calendar</strong> and <strong>contacts</strong></li>
<li>Bookmarks (mobile and browser synchronization thanks to <a href="https://floccus.org/">Floccus</a>)</li>
<li>Nextcloud Office to replace Google Docs, Sheets, etc.</li>
<li>Push notifications from scripts to my phone</li>
</ul>
<p>The mobile app is great for file management. You need a separate app for notes, Nextcloud Notes, and it really sucks. Notes very often appear blank when I open them. I much prefer <a href="https://github.com/quillpad/quillpad">Quillpad</a>, which you can connect to your Nextcloud instance. For contacts and calendar sync, you&rsquo;ll need a separate app like <a href="https://www.davx5.com/">DAVx⁵</a> as Android does not natively support CalDAV/CardDAV, which is frankly absurd in 2026. DAVx⁵ is basically set and forget. My calendar app of choice is <a href="https://github.com/Etar-Group/Etar-Calendar">Etar</a>. For tasks and reminders, I use <a href="https://tasks.org/">Tasks.org</a>.</p>
<p><a href="https://github.com/nextcloud/all-in-one">Get started</a></p>
<p class="center"><img src="https://blog.ampho.fr/images/nextcloud-web.jpg#center" alt="Nextcloud web interface" >

Nextcloud web interface</p>
<p class="center"><img src="https://blog.ampho.fr/images/nextcloud-office.jpg#center" alt="Nextcloud office" >

Nextcloud office</p>
<h3 id="immich---replaces-google-photos">Immich - replaces Google Photos</h3>
<p>Pain level: 1/5 - docker setup, quite straightforward.</p>
<p>Besides basic photo/video storage and viewing, the highlight feature for me is the locally-running face, object and text recognition. Immich can leverage a dedicated GPU for super fast processing if you have one. Otherwise, the CPU will do just fine, albeit a lot slower and less efficiently. It&rsquo;s also super handy to share pictures with friends and family, with the ability to create custom links with a password and an expiry date. Both the web and Android clients are great.</p>
<p><a href="https://immich.app/">Get started</a></p>
<p class="center"><img src="https://blog.ampho.fr/images/immich-web.jpg#center" alt="Immich web client" >

Immich web client</p>
<p class="center"><img src="https://blog.ampho.fr/images/immich-app.jpg#center" alt="Immich Android app" >

Immich Android app, contextual search</p>
<h3 id="navidrome---replaces-spotify">Navidrome - replaces Spotify</h3>
<p>Pain level: 2/5 - docker setup, struggled a bit to find suitable clients for desktop and mobile.</p>
<p>Navidrome is a Subsonic-API piece of software that you can install on a server to stream music from a desktop or a mobile. The unfortunate reality is that self-hosting music is not free as in free or charge, since you need to buy it (Bandcamp, Beatport, CDs&hellip;), but at least it&rsquo;s free as in freedom: no subscription required, no geo-restriction because of copyright and whatnot, you listen on your own terms. You could also sail the high seas, but I&rsquo;m not getting into that. Side note, if you buy music on <a href="https://isitbandcampfriday.com/">Bandcamp Fridays</a>, 100% of the revenue goes to the artist!</p>
<p>It supports on-the-fly reencoding, in case your network conditions do not allow lossless streaming. It offers a web client for desktop, but you can also find thin clients like <a href="https://github.com/jeffvli/feishin">Feishin</a>, which I quite like. As for mobile, there&rsquo;s also a variety of options. I use Synfonium, which you can purchase on the <a href="https://play.google.com/store/apps/details?id=app.symfonik.music.player&amp;hl=fr">Play Store</a> or from the developer directly according to <a href="https://support.symfonium.app/t/how-can-i-pay-for-symfonium-without-google-play/1290">a thread on the forums</a>, although I&rsquo;m not sure if anybody has had success with that recently. Symfonium is <em>very</em> complete and customizable. It really can replace Spotify, local caching of songs included! I especially like the recent addition of the parametric equalizer.</p>
<p><a href="https://github.com/jellyfin/jellyfin">Get started</a></p>
<p class="center"><img src="https://blog.ampho.fr/images/feishin.jpg#center" alt="Feishin" >

Feishin</p>
<p class="center"><img src="https://blog.ampho.fr/images/synfonium.jpg#center" alt="Synfonium Android app" >

Synfonium Android app</p>
<h3 id="jellyfin---replaces-netflix">Jellyfin - replaces Netflix</h3>
<p>Pain level: 4/5 - docker setup, had to reorganize my video library a bit to follow the Jellyfin naming conventions, hardware acceleration can be a bit tricky to setup, stylized subtitles (.ass) can be hit or miss without a bit of fiddling, especially on the native clients, ripping Blu-rays is <strong>not</strong> straightforward.</p>
<p>This one is harder to recommend, because sourcing the required hardware, software and media <em>legally</em> is expensive. You need a specific kind of Blu-Ray drives (LibreDrive compatible) if you&rsquo;re going to go feed your library with physical media, and potentially a lot of storage space. Consider the abundant second-hand market for Blu-rays if you want keep costs down. If you live in the UK, I don&rsquo;t envy you, I just wish we had CEX over here. A good friend of mine took me to a CEX store in Brighton a few years ago and I bought some great movies for just a few pounds (if you&rsquo;re ever reading Lukas, thanks man, CEX is great - don&rsquo;t read that out loud).</p>
<p>You also need decent hardware on the server side to take advantage of all features, especially hardware-accelerated transcoding. Other than that, Jellyfin is mature and works well. The native clients for desktop and mobile are fine. There are some great alternatives out there. I quite like <a href="https://github.com/jarnedemeulemeester/findroid">Findroid</a> (mobile, direct play only, no transcoding) and <a href="https://github.com/DonutWare/Fladder">Fladder</a> (desktop and mobile). Side note, Blu-Ray quality is so, so much better than the über compressed streams on Netflix and such, even after reencoding to save storage space.</p>
<p><a href="https://github.com/jellyfin/jellyfin">Get started</a></p>
<p class="center"><img src="https://blog.ampho.fr/images/jellyfin-linux.jpg#center" alt="Jellyfin Media Player on Linux" >

Jellyfin Media Player on Linux</p>
<p>Resources:</p>
<ul>
<li><a href="https://jellyfin.org/">Jellyfin website</a></li>
<li>Blu-Ray ripping:
<ul>
<li><a href="https://www.makemkv.com/">MakeMKV</a></li>
<li><a href="forum.makemkv.com/forum/viewtopic.php?t=19634">LibreDrive</a></li>
</ul>
</li>
</ul>
<h3 id="stalwart---replaces-gmail">Stalwart - replaces Gmail</h3>
<p>Pain level: great and humbling learning opportinuty, a.k.a. maximum - I knew next to nothing about DMARC, DKIM AND SPF when I installed and setup Stalwart (and still do&hellip;). The documentation is clear, but you need solid fundamentals in all things domain management, certificates, reverse proxy and networking. Careful with your IP reputation!</p>
<p>Once you manage to set it up, Stalwart is great! You can import an export of your Gmail/other provider inbox, although my attempts were unsuccessful (100% a skill issue on my part). I use Thunderbird for my desktop and mobile client needs (Thunderbird for Android is basically <a href="https://k9mail.app/">K-9 Mail</a> rebranded). Don&rsquo;t forget to setup push notifications for incoming e-mails on mobile.</p>
<h2 id="operating-systems">Operating systems</h2>
<h3 id="linux---replaces-windows-even-for-gaming">Linux - replaces Windows, even for gaming</h3>
<p>Pain level: 2/5 - Linux distributions are quite easy to install nowadays, even in dual boot configurations. There are plenty of helpful resources online if you&rsquo;re a newbie. Gaming can be hit or miss, but it&rsquo;s a lot more hit than miss these days (thank you, Valve!)</p>
<p>I&rsquo;ve been using Linux instead of Windows for almost three years now. Probably the best decision I&rsquo;ve ever made. No more pop ups about OneDrive, Copilot, Office or whatever subscription service <em>Microslop</em> wants to shove into my face. No more crazy long update times, slow start menu, crashing Windows Explorer, spying&hellip;</p>
<p>I&rsquo;ve switched to Fedora after more than a year with Debian (both running KDE Plasma for the desktop environment). Everything works great, I have found alternatives to almost all the programs I was used to on Windows when they didn&rsquo;t have a Linux counterpart, and I even game on Linux. Gaming can be a bit tricky, and sometimes impossible with kernel-level anti-cheats - which are an abomination you should avoid like the plague if possible - but it&rsquo;s quite straightforward most of the time, at least with the games I play. I&rsquo;ll to share my experience and tips sometime this year.</p>
<p>Some great resources on YouTube to ease the pain:</p>
<ul>
<li><a href="https://www.youtube.com/@VeronicaExplains">Veronica Explains</a></li>
<li><a href="https://www.youtube.com/@BreadOnPenguins">Bread on Penguins</a></li>
<li><a href="https://www.youtube.com/@TheLinuxEXP">The Linux Experiment</a> - I found his gaming on Linux video very useful</li>
</ul>
<h3 id="grapheneos---replaces-android">GrapheneOS - replaces Android</h3>
<p>Pain level: 2/5 - the installation instructions are very clear and easy, it&rsquo;s just a bit of a pain to wipe everything if your phone is already setup.</p>
<p>GrapheneOS is a privacy and security-focused alternative to Android, built on <a href="https://source.android.com/docs/setup/download">FOSS Android</a>, available for Google Pixel devices only. Yes, you need Google hardware to avoid Google, although this <a href="https://motorolanews.com/motorola-three-new-b2b-solutions-at-mwc-2026/">might change</a> soon. No more Google bloatware/spyware and consequently, quite a lot of storage space reclaimed.</p>
<p>GrapheneOS lets you use sandboxed Google Play Services and the Play Store if you so desire. There are only two caveats to me: some banking apps will refuse to launch because they consider the device insecure - it&rsquo;s not, it&rsquo;s in fact probably more secure than a stock Google Pixel, it&rsquo;s just not &ldquo;certified&rdquo; by Google - and Google Wallet as well as most other NFC payment apps will refuse to work.</p>
<h2 id="other-stuff">Other stuff</h2>
<h3 id="llms---or-ai">LLMs - or &ldquo;&ldquo;&ldquo;AI&rdquo;&rdquo;&rdquo;</h3>
<p>Pain level: 0/5 - non US-based alternatives to LLMs exist, and they work great for my use cases.</p>
<p>I only sometimes use LLMs - mainly for translations in Japanse and Korean and to check for correctness in English - but I avoid OpenAI, Anthropic and the likes when I do. Those companies are literally evil (I&rsquo;m not going to explain why, just watch interviews of their top people and read their tweets, you&rsquo;ll understand).</p>
<p>You can definitely self-host an LLM, and there are plenty of ressources online to help you do that, but I found myself relying on online services as of late because my hardware is not capable enough for accurate responses. My picks are <a href="https://lumo.proton.me/">Lumo by Proton</a> and <a href="https://chat.mistral.ai/chat">Mistral</a>.</p>
<p>The former is privacy respecting, the latter is French, both are plenty for my use cases and have mobile clients. The Lumo app is okay (probably a web wrapper/Chromium-based app), and I&rsquo;ve never tried Mistral&rsquo;s.</p>
<p><strong>Update 2026-03-26</strong>: I&rsquo;ve started using <a href="https://ollama.com/library/translategemma">translategemma</a> for translation tasks, and it&rsquo;s been working great. The 12b model fits in my 11GB or VRAM, and the inference is super fast (once the model is loaded). The translations seem accurate after checking manually. I use <a href="https://github.com/open-webui/open-webui">Open WebUI</a> as a web interface to interact with the LLM. The UI is responsive (adapts to displays of different sizes, desktop, mobile&hellip;).</p>
<h3 id="futo-keyboard---replaces-gboard-and-co">FUTO Keyboard - replaces Gboard and co</h3>
<p><em>Added on 2026-03-11</em>: pain level: 1/5 - Requires a tiny bit for dictionaries and customization to your linking.</p>
<p>All the data you output, private or otherwise, goes through your keyboard app. For example Gboard, the default on Google Pixel devices, or Samsung Keyboard on Samsung devices. If you don&rsquo;t trust themn, you can opt for an open-source and privacy friendly alternative, FUTO Keyboard. FUTO is fully offline, even for voice input, which is based on OpenAI Whisper. Note that the app will ask you for a financial contribution before you can use that feature. Even though it&rsquo;s still an alpha at the time of writing, the app is very stable and featureful.</p>
<p><a href="https://keyboard.futo.org/">Get started</a></p>
<h2 id="things-i-havent-been-able-to-replace">Things I haven&rsquo;t been able to replace</h2>
<h3 id="google-maps-and-youtube">Google Maps and YouTube</h3>
<p>The crowd-sourced nature of the data present in Google Maps combined with the sheer amount of users makes the app very powerful and hard to compete with. I use it to discover things to visit, check out menus, reviews, photos of places&hellip; I don&rsquo;t think any FOSS competitor exists as far as the data aspect is concerned. The only exceptions I know are Naver and Kakao Maps, which are widely used in South Korea. Google Maps is kind of useless there, as their laws prevent the app from providing navigation and accurate satellite imagery. I also found the data on the Korean apps more plentiful and up to date.</p>
<p><strong>Update 2026-03-11</strong>: Google Maps will soon be fully functionnal according to <a href="https://www.reuters.com/world/asia-pacific/south-korea-approves-google-bid-export-high-precision-map-data-2026-02-27/">Reuters</a>.</p>
<p>As for YouTube, well, it&rsquo;s virtually a monopoly. Most of the content creators I like upload their videos there. Although, I make my experience with the app and website less painful and ad-free thanks to <a href="https://revanced.app/">Revanced</a>, <a href="https://sponsor.ajay.app/">SponsorBlock</a> and <a href="https://github.com/InfinityLoop1308/PipePipe">PipePipe</a>.</p>
<h3 id="social-media-and-messaging-apps">Social media and messaging apps</h3>
<p>I use WhatsApp, Instagram and Discord. I would love to delete my accounts on those platforms, but I also want to stay in touch with family and friends, the vast majority of whom are not willing to switch to more privacy respecting services like Bluesky, Matrix, Signal, etc., which I understand and respect.</p>
<p>I actually had deleted Instagram altogether in 2024, but decided to create another account to keep in touch with the people I spontaneously connect with. For instance I was fortunate enough to travel to South Korea last month and I made a friend there. We&rsquo;re keeping in touch on Instagram! 서인, if you&rsquo;re reading this, 안녕, you&rsquo;re very cool and I&rsquo;m glad we met :).</p>
<p>I may have lied, about this being a &ldquo;quick summary&rdquo;. My bad.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Share text to all devices on LAN</title>
      <link>https://blog.ampho.fr/posts/share-text-lan/</link>
      <pubDate>Tue, 02 Dec 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/share-text-lan/</guid>
      <description>Easily share text and notes to all devices on your local network.</description>
      <content:encoded><![CDATA[<p>I often find myself needing to share text from a device to another, like a link from my phone to my laptop for instance. I thought, wouldn&rsquo;t it be great if I had a simple webpage where I could just paste text from a device, so that it could be read from another? Some sort of LAN clipboard, if you will.</p>
<p>I could use Nextcloud notes as a medium, but that requires me to login, and it would be a pain to log out and login every time I need to just share some text on a shared/untrusted target devices. So it&rsquo;s important that this hypothetical webpage does not require authentication.</p>
<p>That&rsquo;s where <a href="https://github.com/dullage/flatnotes">flatnotes</a> comes in. It can easily be installed with <a href="https://github.com/dullage/flatnotes?tab=readme-ov-file#example-docker-compose">Docker compose</a>, and authentication can be disabled by setting the <strong>FLATNOTES_AUTH_TYPE</strong> environment variable to <strong>none</strong>.</p>
<p>Last but not least, I want to restrict the service to LAN only. If you use Caddy, that can be done quite easily thanks to snippets. I covered this exact use case <a href="https://blog.ampho.fr/posts/caddy-restrict-to-local/">here</a>.</p>
<p>And that&rsquo;s it! Easily accessible text content from and to any device on LAN with a web browser. Here&rsquo;s what it looks like:
<br>
<br>
<em>Note that the images are quite cropped in, hence the strange looking proportions.</em></p>
<p class="center"><img src="https://blog.ampho.fr/images/flatnotes-pc.jpg#center" alt="FlatnotesPC" >

This is the home page where recent notes are displayed.</p>
<br>
<p class="center"><img src="https://blog.ampho.fr/images/flatnotes-phone.jpg#center" alt="FlatnotesPhone" >

And this is what the inside of a note looks like from a handheld.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Remove EXIF data before sharing a picture in Android</title>
      <link>https://blog.ampho.fr/posts/scramble-exif/</link>
      <pubDate>Wed, 26 Nov 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/scramble-exif/</guid>
      <description>Doing so is probably a good idea.</description>
      <content:encoded><![CDATA[<p>I found a neat little Android app the other day that removes EXIF data from pictures before you share them. It&rsquo;s called <strong>Scrambled Exif</strong> and it&rsquo;s available on <a href="https://f-droid.org/packages/com.jarsilio.android.scrambledeggsif/">F-Droid</a>. The name is a bit misleading, since it really does remove EXIF data altogether instead of just scrambling it.</p>
<p>The way it works is simple: when using the share menu with a picture in Android, select Scrambled Exif instead of the destination app where you want the picture shared. Scrambled Exif removes the metadata and displays another share menu, where you choose where the EXIF-less picture should go. And yes, it works for multiple images too!</p>
<p>This might not be necessary, since popular messaging apps such as WhatsApp and Discord seem to use tools like oxipng and jpegoptim (<a href="https://blog.ampho.fr/posts/make-images-small-web/">covered in this post!</a>) to compress images and remove metadata before they are sent. But you never know, maybe the EXIF data is actually read and stored somewhere before compression&hellip; That wouldn&rsquo;t even surprise me to be honest.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Track chapters in Poweramp</title>
      <link>https://blog.ampho.fr/posts/lyricsmd3-chapters-poweramp/</link>
      <pubDate>Wed, 12 Nov 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/lyricsmd3-chapters-poweramp/</guid>
      <description>Hijack ID3 tags to get chapters in long tracks in Poweramp.</description>
      <content:encoded><![CDATA[<p>Sifting through a several hours-long DJ set, concert or podcast to find a specific portion is a pain. If you happen to store your tracks locally and use Poweramp as a music player on your Android device (probably something like 0.000001% of Earth&rsquo;s population), there is a solution - or small hack, whatever you want to call it.</p>
<p>You can use the &ldquo;LYRICS&rdquo; ID3 tag to store chapters as if they were lyrics:</p>
<pre tabindex="0"><code>[00:00.0]Overture x The Bat Overture
[01:57.0]Ember x Emperor Waltz
[05:22.0]All Night x Éljen a Magyar
...
[71:19.0]Other Side
[77:33.0]No Tomorrow x The Baron
[97:13.0]Sientelo
</code></pre><p>I usually get chapter timestamps from YouTube comments, and I and use <a href="https://www.mp3tag.de/en/index.html">Mp3tag</a> to edit ID3 tags (through Wine).</p>
<p class="center"><img src="https://blog.ampho.fr/images/mp3tag.png#center" alt="Mp3Tag" >

You must enable the &ldquo;LYRICS&rdquo; field in the settings first.</p>
<p>You unfortunately must stick to minutes and seconds to format the timestamps. For example: Poweramp will not recognize <code>[01:11:19.0]</code> as a timestamp. <code>[71:19.0]</code> will work, though. I&rsquo;m not sure if this is a ID3 specification thing, or a Poweramp quirk. Here&rsquo;s what it looks like in the app:</p>
<p class="center"><img src="https://blog.ampho.fr/images/poweramp.png#center" alt="Poweramp" >

Each &ldquo;chapter&rdquo; is clickable!</p>
<p>Poweramp is IMHO the best music player for Android out there. There is a trial version on the <a href="https://play.google.com/store/apps/details?id=com.maxmpz.audioplayer">PlayStore</a>. The full version is available on there as well, but you can support the dev directly by purchasing it on the <a href="https://powerampapp.com/">official website</a> (no Google tax, and will run on degooggled devices).</p>
]]></content:encoded>
    </item>
    <item>
      <title>Disable wakeup triggers on Debian</title>
      <link>https://blog.ampho.fr/posts/disable-wakeup-triggers-debian/</link>
      <pubDate>Mon, 27 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/disable-wakeup-triggers-debian/</guid>
      <description>A startup script to disable all wakeup triggers.</description>
      <content:encoded><![CDATA[<p>Here&rsquo;s a little systemd unit and script to disable ACPI-registered wakeup sources automatically at startup. It&rsquo;s quick and dirty, but has the advantage of being set-and-forget. No more waking up your computer by accident with your mouse, keyboard or otherwise. The power button still works, though.</p>
<p><a href="https://git.ampho.fr/anhminhpho/disable-acpi-wakeup">https://git.ampho.fr/anhminhpho/disable-acpi-wakeup</a></p>
<p>The systemd unit calls this bit of code, which disables all wakeup triggers associated with currently connected peripherals:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bash" data-lang="Bash"><span style="display:flex;"><span><span style="color:#66d9ef">for</span> d in <span style="color:#66d9ef">$(</span>cat /proc/acpi/wakeup | grep enabled | awk <span style="color:#e6db74">&#39;{print $1}&#39;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>	echo $d &gt; /proc/acpi/wakeup
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><p>Instructions to install are available in the Git repo. Tested on Debian 12 and 13, but it probably works on most distros.</p>
<h2 id="references">References</h2>
<p><a href="https://wiki.archlinux.org/title/Power_management/Wakeup_triggers#/proc/acpi/wakeup">https://wiki.archlinux.org/title/Power_management/Wakeup_triggers#/proc/acpi/wakeup</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Join Kasa and Tapo devices to your network without a TP-Link account</title>
      <link>https://blog.ampho.fr/posts/python-kasa/</link>
      <pubDate>Thu, 23 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/python-kasa/</guid>
      <description>No need for yet another account to use your Kasa and Tapo smart devices thanks to python-kasa.</description>
      <content:encoded><![CDATA[<p>You&rsquo;re telling me I need yet another account to use my Tapo and Kasa &ldquo;smart&rdquo; devices? Thanks, but no thanks. Here&rsquo;s how to connect these to your Wi-Fi network without giving out personal information. This post assumes you have a Home Assistant instance up and running to manage your IoT devices.</p>
<h2 id="install-python-kasa">Install python-kasa</h2>
<p><code>pip install python-kasa</code></p>
<h2 id="join-device-to-your-home-network">Join device to your home network</h2>
<p>Some devices might <a href="https://python-kasa.readthedocs.io/en/stable/SUPPORTED.html">require authentication</a>, mine does not.</p>
<p>Scan for Wi-Fi access points:</p>
<pre tabindex="0"><code>$ kasa --host 192.168.0.1 wifi scan

Discovering device 192.168.0.1 for 10 seconds
Scanning for wifi networks, wait a second..
Found 5 wifi networks!
         WifiNetwork(ssid=&#39;&lt;SSID&gt;&#39;, key_type=&#39;wpa2_psk&#39;, cipher_type=2, bssid=&#39;1E4D73E63290&#39;, channel=8, rssi=None, signal_level=2)
         ...
</code></pre><p>Scanning might fail. I got the following error after a reset of my Kasa HS100 (can&rsquo;t rememeber if scaning worked the first time):</p>
<pre tabindex="0"><code>Raised error: Error on smartlife.iot.common.softaponboarding.get_scaninfo: {&#39;err_code&#39;: -1, &#39;err_msg&#39;: &#39;module not support&#39;}
</code></pre><p>In that case, join your network directly:</p>
<pre tabindex="0"><code>kasa --host 192.168.0.1 wifi join &#34;&lt;SSID&gt;&#34;
</code></pre><p>The keytypes are as follows:</p>
<blockquote>
<p>0 - Open<br>
1 - WEP<br>
2 - WPA<br>
3 - WPA2<br>
4 - WPA3</p></blockquote>
<p>Your device is now joined, you can add it in your Home Assistant instance to manage it.</p>
<p>If you ever change your Wi-Fi SSID and/or password: reset your Tapo/Kasa smart devices, join them to your network, and Home Assistant will recognize them automatically. They will be usable again right after joining.</p>
<h2 id="tapo-cameras-nov-2025-update">Tapo cameras (Nov. 2025 update)</h2>
<p>I got my hands on Tapo C210 indoor cameras, and they unfortunately must be setup in the Tapo app with a TP-Link account before they can be used in Home Assistant. You must enable &ldquo;Third-Party Compatibility&rdquo; under &ldquo;Services&rdquo; in the &ldquo;Me&rdquo; menu, as well as create a &ldquo;Camera Account&rdquo; under &ldquo;Advanced Settings&rdquo; <strong>for each of your cameras</strong>. Only then will you be able to add them into the TP-Link integration in Home Assistant.</p>
<p>While you have the app installed, make sure to update the firmwares and reboot the cameras after making the abovementioned changes (I couldn&rsquo;t login in the integration at first, maybe the reboot helped, not sure).</p>
<p>The TP-Link integration unfortunately lacks a lot of features. Instead, you can take a look at <a href="https://github.com/JurajNyiri/HomeAssistant-Tapo-Control">Juraj Nyíri&rsquo;s integration</a>, which is specific to Tapo cameras and offers much more control. Your TP-Link password is required during setup, but the integration remains fully local according to the Readme. This means you can probably delete your account after adding your Tapo devices to HA. That&rsquo;s probably true for the native integration as well, but I didn&rsquo;t test it.</p>
<p>The app is however still useful to configure some settings not available in HA like framerate. If you no longer need it, you can archive it in recent versions of Android instead of uninstalling it completely, so that your account data remains on the device (no need to login again). And for good measure, I blocked any traffic from my cameras to WAN and vice-versa in my router.</p>
<h2 id="references">References</h2>
<p><a href="https://python-kasa.readthedocs.io/">https://python-kasa.readthedocs.io/</a></p>
<p><a href="https://github.com/python-kasa/python-kasa/issues/1325">https://github.com/python-kasa/python-kasa/issues/1325</a></p>
<p><a href="https://www.home-assistant.io/integrations/tplink#troubleshooting">https://www.home-assistant.io/integrations/tplink#troubleshooting</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Make images small for the web</title>
      <link>https://blog.ampho.fr/posts/make-images-small-web/</link>
      <pubDate>Tue, 14 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/make-images-small-web/</guid>
      <description>Reduce the size of images without sacrificing quality.</description>
      <content:encoded><![CDATA[<p>While writing my <a href="https://blog.ampho.fr/posts/gits/">latest post</a>, I realized the number of images might make loading quite slow, and that&rsquo;s when I found out about <a href="https://github.com/tjko/jpegoptim">jpegoptim</a> and <a href="https://github.com/oxipng/oxipng">Oxipng</a>. Those tools can, losslessly, significantly reduce the size of JPEG and PNG files (among others) thanks to <del>black magic</del> operations involving the Huffman algorithm, as well as stripping metadata.</p>
<p>There are a number of GUIs that make use of those utilites. I personally like <a href="https://flathub.org/en/apps/org.kde.optiimage">OptiImage</a>:</p>
<p class="center"><img src="https://blog.ampho.fr/images/optiimage.png" alt="OptiImage" >

72% decrease in size from a digital camera. Not bad!</p>
<p>Keep in mind that you might mess up the orientation of your pictures by stripping metadata. You can either rotate them &ldquo;for real&rdquo; based on the EXIF rotation data before minimizing them, or use a tool to restore the correct orientation after the fact. I prefer the second option because I&rsquo;m lazy: I use <a href="https://www.xnview.com/en/xnview/">XnView</a>🇫🇷, mainly because it supports JPEG lossless rotation and batch processing.</p>
<p>I also chose to display thumbnails on the page instead of the full pictures to make loading even faster. Clicking on the image reveals the full-size image. I made the thumbnails manually by downsizing the pictures and passing them through jpegoptim as well.</p>
<p>The result is quite nice: a lot of thumbnails for only 1.79 MB of total data transferred (no cache), and faster loading of full-size images.</p>
<p><em>I tried to reduce the footprint of pictures stored by WhatsApp on my phone, but it appears the app already uses jpegoptim as OptiImage was not able to reduce their size further.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Ghost in the Shell: Stand Alone Complex Exhibit</title>
      <link>https://blog.ampho.fr/posts/gits/</link>
      <pubDate>Wed, 08 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/gits/</guid>
      <description>An art sales event in Paris, by GAAAT.</description>
      <content:encoded><![CDATA[<p>Got word of a GitS art exhibit/sales event taking place in Paris thanks to a friend. Couldn&rsquo;t miss it for anything! I went there with a friend, came back two days later with a camera, and the following day with three other friends&hellip; Am I obsessed with the franchise? Yeah, maybe.</p>
<p>The artworks are a creation of <a href="https://gallery.gaaat.com/en">GAAAT</a>, a Tokyo-based art gallery. The event is hosted by the DANAE gallery, from October 2nd to October 9th, and is the result of a partnership with <a href="https://la-meduse-violette.com/">La Méduse Violette</a>. It was supposed to end on the 5th, but was extended thanks to its popularity.</p>
<p>Every piece has depth thanks to a layered printing process on metal canvas, with up to 32 layers. Most are extremely detailed and impressive. The effect is especially striking when you move around a little bit to see how the light catches on differently depending on your position.</p>
<p>Thank you very much to the GAAAT staff, as well as Olivia from La Méduse Violette, for your kindness. I hope to see you again at another GAAAT event!</p>
<p>Here are a few (amateur) pictures of my favorite pieces and angles. Click on an image to see it full-size. Please ignore the lens distorsion and poor framing, I barely ever take pictures with an actual camera lol.
<em>Feel free to zoom-in to appreciate the small details and texture revealed by the reflections</em>.</p>
<hr>
<p class="center"><strong>Re:compile</strong></p>
<p><a href="/posts/gits/rotate-Recompile1-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-rotate-Recompile1-min.jpg#center" alt="Recompile1" >
</a>
<a href="/posts/gits/Recompile2-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-Recompile2-min.jpg#center" alt="Recompile2" >
</a>
<a href="/posts/gits/Recompile3-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-Recompile3-min.jpg#center" alt="Recompile3" >
</a></p>
<hr>
<p class="center"><strong>Threshold</strong></p>
<p><a href="/posts/gits/Threshold1-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-Threshold1-min.jpg#center" alt="Threshold1" >
</a>
<a href="/posts/gits/Threshold2-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-Threshold2-min.jpg#center" alt="Threshold2" >
</a>
<a href="/posts/gits/Threshold3-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-Threshold3-min.jpg#center" alt="Threshold3" >
</a></p>
<hr>
<p class="center"><strong>Steel Hearts</strong></p>
<p><a href="/posts/gits/SteelHearts1-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-SteelHearts1-min.jpg#center" alt="SteelHearts1" >
</a>
<a href="/posts/gits/SteelHearts2-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-SteelHearts2-min.jpg#center" alt="SteelHearts1" >
</a></p>
<hr>
<p class="center"><strong>Mimesis - MOTOKO, BATOU, TACHIKOMA</strong></p>
<p><a href="/posts/gits/Mimesis1-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-Mimesis1-min.jpg#center" alt="Mimesis1" >
</a>
<a href="/posts/gits/Mimesis2-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-Mimesis2-min.jpg#center" alt="Mimesis2" >
</a>
<a href="/posts/gits/Mimesis3-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-Mimesis3-min.jpg#center" alt="Mimesis3" >
</a></p>
<hr>
<p class="center"><strong>Hurry Up!</strong></p>
<p><a href="/posts/gits/HurryUp1-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-HurryUp1-min.jpg#center" alt="HurryUp1" >
</a>
<a href="/posts/gits/HurryUp2-min.JPG"><img src="https://blog.ampho.fr/posts/gits/thumb-HurryUp2-min.jpg#center" alt="HurryUp2" >
</a></p>
<details>
<summary>Did you catch those?</summary>
<p>Small Tachikomas in Batou&rsquo;s collar (Thank you Kayo for pointing it out! [Kayo was part of the GAAAT team present])</p>
<p>The Laughing Man on Motoko&rsquo;s sunglasses</p>
<p>The tiny scenes from the anime on up-close shots</p>
<p>You might recognize Re:compile and Threshold if you&rsquo;ve browsed the entries of S.A.C. and S.A.C. 2nd GIG on Anilist</p>
</details>
]]></content:encoded>
    </item>
    <item>
      <title>Random Information Dump</title>
      <link>https://blog.ampho.fr/posts/random-internet-information-dump/</link>
      <pubDate>Sun, 05 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/random-internet-information-dump/</guid>
      <description>As the title says.</description>
      <content:encoded><![CDATA[<h2 id="music">Music</h2>
<h3 id="junglednb">Jungle/DnB</h3>
<ul>
<li>The &ldquo;Yeah! Whoo!&rdquo; heard in many Jungle and Drum &amp; Bass songs come from the &ldquo;Think Break&rdquo;, sampled from <a href="https://youtu.be/HKix_06L5AY?si=ZUz6oBZ3ktNRcpQQ&amp;t=82">&ldquo;Think (About It)&rdquo;</a> by Lyn Collins (1972).</li>
</ul>
<h3 id="ghost-in-the-shell-stand-alone-complex">Ghost in the Shell: Stand Alone Complex</h3>
<ul>
<li>
<p>&ldquo;take a little end&rdquo; plays towards the end of the flashback section in episode 11 of 2nd GIG, according to <a href="https://tvtropes.org/pmwiki/pmwiki.php/TearJerker/GhostInTheShellStandAloneComplex">tvtropes</a>.</p>
</li>
<li>
<p>&ldquo;Dew&rdquo; plays in episode 17 of 2nd GIG according to a comment thread in <a href="https://www.youtube.com/watch?v=zLiWov6aAKc">this YouTube upload</a>. It starts around the 2:40 minutes mark, although it&rsquo;s an instrumental version.
<img src="https://blog.ampho.fr/images/Dew.png" alt="YouTube comments" >
</p>
</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Enable Windows 10 Extended Support</title>
      <link>https://blog.ampho.fr/posts/enable-w10-extended-support/</link>
      <pubDate>Fri, 03 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/enable-w10-extended-support/</guid>
      <description>Microsoft is ending support for Windows 10 on October 14, 2025. Here&amp;#39;s how to enable Extended Support Updates (ESU).</description>
      <content:encoded><![CDATA[<p>If you don&rsquo;t own a Windows 11-compatible computer, or simply don&rsquo;t want to upgrade to that shitty, ad-ridden, spyware of an OS, <a href="https://massgrave.dev/">Massgrave</a> is the way to go. It&rsquo;s an <a href="https://github.com/massgravel/Microsoft-Activation-Scripts">open-source</a> interactive Batch script that lets you enable ESU on a Windows 10 computer, for free, no Microsoft account required. Here&rsquo;s how it looks like at the time of writing. <strong>TL;DR, use the TSforge option in Massgrave</strong>.</p>
<p>Massgrave provides this PowerShell one-liner that runs the script after performing some checks. Make sure you are up to date, it might fail otherwise.</p>
<pre tabindex="0"><code>irm https://get.activated.win | iex
</code></pre><p><a href="https://get.activated.win">https://get.activated.win</a> contains the PowerShell code that performs those check. <code>irm</code> is short for <code>Invoke-RestMethod</code>, which downloads the script. <code>iex</code> is short for <code>Invoke-Expression</code>, which runs the script.</p>
<p>The actual Batch script is available at multiple location stored in the <code>URLs</code> array. The preliminary PowerShell script randomly selects a location to download it from and run it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-PowerShell" data-lang="PowerShell"><span style="display:flex;"><span>$URLs = @(
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;https://raw.githubusercontent.com/massgravel/Microsoft-Activation-Scripts/ab6b572af940fa0ea4255b327eb6f69a274d6725/MAS/All-In-One-Version-KL/MAS_AIO.cmd&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;https://dev.azure.com/massgrave/Microsoft-Activation-Scripts/_apis/git/repositories/Microsoft-Activation-Scripts/items?path=/MAS/All-In-One-Version-KL/MAS_AIO.cmd&amp;versionType=Commit&amp;version=ab6b572af940fa0ea4255b327eb6f69a274d6725&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;https://git.activated.win/massgrave/Microsoft-Activation-Scripts/raw/commit/ab6b572af940fa0ea4255b327eb6f69a274d6725/MAS/All-In-One-Version-KL/MAS_AIO.cmd&#39;</span>
</span></span><span style="display:flex;"><span>    )
</span></span></code></pre></div><p>The long string <code>ab6b572af940fa0ea4255b327eb6f69a274d6725</code> is the commit hash where it originates from.</p>
<p>The script is <strong>very</strong> big. Massgrave provides a breakdown on <a href="https://github.com/massgravel/Microsoft-Activation-Scripts/tree/master/MAS/Separate-Files-Version">GitHub</a>. I frankly didn&rsquo;t dive deeper into the code. I just know it&rsquo;s trusted by countless people, and my locally-running LLM as well as Mistral AI didn&rsquo;t find anything suspicious (except for the fact that it bypasses official activation channels&hellip;).</p>
<p>To enable ESU, choose option 3, TSforge:
<img src="https://blog.ampho.fr/images/Massgrave.png" alt="Massgrave" >
</p>
<p>The process can take a few minutes. You&rsquo;ll know it&rsquo;s done when it tells you to press a key to return to the main menu. It&rsquo;s that easy! No e-waste and money saved.</p>
<p><strong>Note</strong>: According to <a href="https://next.ink/201508/windows-10-obtient-un-an-de-sursis-en-europe-en-quelque-sorte/">Next</a>, formerly Next INpact, European users can benefit from ESU by logging in with a Microsoft account. But that &ldquo;solution&rdquo; is obviously a privacy nightmare. Maybe it&rsquo;s a good time to switch to&hellip;</p>
<pre tabindex="0"><code> █████        ███                                   
░░███        ░░░                                    
 ░███        ████  ████████   █████ ████ █████ █████
 ░███       ░░███ ░░███░░███ ░░███ ░███ ░░███ ░░███ 
 ░███        ░███  ░███ ░███  ░███ ░███  ░░░█████░  
 ░███      █ ░███  ░███ ░███  ░███ ░███   ███░░░███ 
 ███████████ █████ ████ █████ ░░████████ █████ █████
░░░░░░░░░░░ ░░░░░ ░░░░ ░░░░░   ░░░░░░░░ ░░░░░ ░░░░░  (?)
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Restrict subdomain access to LAN only with Caddy</title>
      <link>https://blog.ampho.fr/posts/caddy-restrict-to-local/</link>
      <pubDate>Mon, 29 Sep 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/caddy-restrict-to-local/</guid>
      <description>Any subdomain you want to use locally only? Easily block access from WAN with Caddy as a reverse proxy.</description>
      <content:encoded><![CDATA[<p>There are subdomains on my home network that I only want accessible locally. While there are no public DNS records pointing to them, the URLs are still visible thanks to Certificate Transparency, and I want to add a layer of security just to be sure.</p>
<p>Here&rsquo;s where Caddy snippets are convenient to block all WAN traffic for select subdomains. The following snippet in my Caddyfile blocks all traffic coming from outside the subnets after <code>remote_ip</code>, in the blocks where it&rsquo;s used.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-caddy" data-lang="caddy"><span style="display:flex;"><span><span style="color:#75715e"># Snippet
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>(lan_only) {<span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        # Allow traffic from my local network, including wg-easy
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#a6e22e">@external</span> <span style="color:#66d9ef">not</span> <span style="color:#66d9ef">remote_ip</span> <span style="color:#ae81ff">10</span><span style="color:#e6db74">.0.0.0/24</span> <span style="color:#ae81ff">10</span><span style="color:#e6db74">.42.42.0/24</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">abort</span> <span style="color:#a6e22e">@external</span>
</span></span><span style="display:flex;"><span>}<span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># Protected subdomain
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        @subdomain host subdomain.ampho.fr
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">handle</span> <span style="color:#a6e22e">@subdomain</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">import</span> lan_only<span style="color:#75715e"> # Import the snippet
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                <span style="color:#66d9ef">reverse_proxy</span> localhost:<span style="color:#ae81ff">1234</span>
</span></span><span style="display:flex;"><span>        }
</span></span></code></pre></div><p>Don&rsquo;t forget to reload the Caddy service after modifying the Caddyfile.</p>
<p>In my case, name resolution is done thanks to entries in the <strong>hosts</strong> file in my OpenWrt router from GL.iNet. GL.iNet offers a convenient interface to modify that file without having to SSH into the router. Example entry:</p>
<pre tabindex="0"><code>10.0.0.3 notes.ampho.fr
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Caddy HTTPS with the OVH DNS plugin</title>
      <link>https://blog.ampho.fr/posts/caddy-ovh-tls/</link>
      <pubDate>Fri, 26 Sep 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/caddy-ovh-tls/</guid>
      <description>Get a wildcard certificate with the DNS challenge in Caddy and OVH as a registrar.</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve recently started using subdomains for services I only access locally. I want Caddy to serve them through HTTPS so I don&rsquo;t get annoying warnings about the connection being insecure, while blocking access from WAN just to be extra safe, which I cover <a href="https://blog.ampho.fr/posts/caddy-restrict-to-local/">here</a>.</p>
<p>The thing is, port 80 is not forwarded on my router, which is required for the ACME HTTP-01 challenge. <del>I also don&rsquo;t want a DNS record for my private subdomains to appear publicly</del> (I use the hosts file in my router to provide DNS resolution locally). EDIT: <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Certificate_Transparency">Certificate Transparency</a> is a thing&hellip; Thank you Laurent!</p>
<p>A convenient solution is to use the ACME DNS challenge to get a wildcard certificate. Here&rsquo;s how to do it with OVH.</p>
<h2 id="install-caddy-with-the-ovh-plugin">Install Caddy with the OVH plugin</h2>
<p>Install a custom build of Caddy with the OVH plugin. I went with the <a href="https://caddyserver.com/docs/build#package-support-files-for-custom-builds-for-debianubunturaspbian">update-alternatives from source</a> route after installing vanilla Caddy through my package manager, but there are several other ways to do it, more info on that page.</p>
<h2 id="ovh-api-credentials">OVH API credentials</h2>
<p>You now have to create OVH API keys from the <a href="https://eu.api.ovh.com/createToken/">dedicated admin page</a> with the correct API permissions. According to the libdns <a href="https://github.com/libdns/ovh#authenticating">GitHub repo</a>, they are as follows for a single domain:</p>
<pre tabindex="0"><code>GET /domain/zone/ampho.fr/record
POST /domain/zone/ampho.fr/record

GET /domain/zone/ampho.fr/record/*
PUT /domain/zone/ampho.fr/record/*
DELETE /domain/zone/ampho.fr/record/*

GET /domain/zone/ampho.fr/soa
POST /domain/zone/ampho.fr/refresh
</code></pre><p>Once you have your API credentials, store them in a location where Caddy will be able to read them. I use a <strong>.env</strong> file in <code>/etc/caddy/</code> and a <strong>service override</strong> as described in the <a href="https://caddyserver.com/docs/running#overrides">Caddy documentation</a>.</p>
<p>Here is what my <strong>.env</strong> file looks like. Change the endpoint <a href="https://github.com/ovh/go-ovh?tab=readme-ov-file#application-keyapplication-secret">according to your region</a>.</p>
<pre tabindex="0"><code>OVH_ENDPOINT=ovh-eu
OVH_APPLICATION_KEY=xxxxxxxxxxxxxxxxx
OVH_APPLICATION_SECRET=xxxxxxxxxxxxxxxxx
OVH_CONSUMER_KEY=xxxxxxxxxxxxxxxxx
</code></pre><p>Don&rsquo;t forget about the service override!</p>
<h2 id="caddyfile">Caddyfile</h2>
<p>You can now modify your <a href="https://caddyserver.com/docs/caddyfile/patterns#wildcard-certificates">Caddyfile</a> to use the DNS challenge and serve all your subdomains with a wildcard certificate:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-caddy" data-lang="caddy"><span style="display:flex;"><span>*.ampho.fr {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">tls</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">dns</span> <span style="color:#e6db74">ovh</span> {
</span></span><span style="display:flex;"><span>                        <span style="color:#66d9ef">endpoint</span> <span style="color:#ae81ff">{$OVH_ENDPOINT}</span><span style="color:#75715e"> # Variables from the .env file
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                        <span style="color:#66d9ef">application_key</span> <span style="color:#ae81ff">{$OVH_APPLICATION_KEY}</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#66d9ef">application_secret</span> <span style="color:#ae81ff">{$OVH_APPLICATION_SECRET}</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#66d9ef">consumer_key</span> <span style="color:#ae81ff">{$OVH_CONSUMER_KEY}</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>        }<span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        #My service
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#a6e22e">@subdomain</span> <span style="color:#66d9ef">host</span> <span style="color:#e6db74">subdomain.ampho.fr</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">handle</span> <span style="color:#a6e22e">@subdomain</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">reverse_proxy</span> localhost:<span style="color:#ae81ff">1234</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">...</span>
</span></span></code></pre></div><p>Make sure to restart/reload Caddy to apply the changes. Check the service health with <code>journalctl -xeu caddy.service</code>. If it&rsquo;s healthy, you&rsquo;re done!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Check the integrity of your content library with FFmpeg</title>
      <link>https://blog.ampho.fr/posts/check-library-integrity/</link>
      <pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/posts/check-library-integrity/</guid>
      <description>&lt;p&gt;I recently stumbled upon corrupted music files randomly during playback. My player (Poweramp) would skip them before the end, and I was worried more might be corrupted and started looking for a way to easily check my whole library. FFmpeg is great for the job (of course it is). Here&amp;rsquo;s the command I ended up with after browsing multiple Stack Overflow threads.&lt;/p&gt;
&lt;p&gt;The command recursively checks FLAC files in the current directory. You can adjust the file extension, or remove it entirely to check all files. Add &lt;code&gt;-maxdepth 1&lt;/code&gt; after &lt;code&gt;find ./&lt;/code&gt; to disable recursion. You might want to run it in &lt;a href=&#34;https://linuxize.com/post/how-to-use-linux-screen/&#34;&gt;Screen&lt;/a&gt; since the process will read through your entire library, which could take some time.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I recently stumbled upon corrupted music files randomly during playback. My player (Poweramp) would skip them before the end, and I was worried more might be corrupted and started looking for a way to easily check my whole library. FFmpeg is great for the job (of course it is). Here&rsquo;s the command I ended up with after browsing multiple Stack Overflow threads.</p>
<p>The command recursively checks FLAC files in the current directory. You can adjust the file extension, or remove it entirely to check all files. Add <code>-maxdepth 1</code> after <code>find ./</code> to disable recursion. You might want to run it in <a href="https://linuxize.com/post/how-to-use-linux-screen/">Screen</a> since the process will read through your entire library, which could take some time.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>IFS<span style="color:#f92672">=</span><span style="color:#e6db74">$&#39;\n&#39;</span>
</span></span><span style="display:flex;"><span>set -f
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> f in <span style="color:#66d9ef">$(</span>find ./ -name <span style="color:#e6db74">&#39;*.flac&#39;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">do</span> ffmpeg -v error -i <span style="color:#e6db74">&#34;</span>$f<span style="color:#e6db74">&#34;</span> -f null - |&amp; sed -e <span style="color:#e6db74">&#34;1i </span>$f<span style="color:#e6db74">&#34;</span> -e <span style="color:#e6db74">&#39;$a\---&#39;</span> | tee -a error.log
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span><span style="display:flex;"><span>unset IFS
</span></span><span style="display:flex;"><span>set +f
</span></span></code></pre></div><p>One-liner:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>IFS<span style="color:#f92672">=</span><span style="color:#e6db74">$&#39;\n&#39;</span>; set -f; <span style="color:#66d9ef">for</span> f in <span style="color:#66d9ef">$(</span>find ./ -name <span style="color:#e6db74">&#39;*.flac&#39;</span><span style="color:#66d9ef">)</span>; <span style="color:#66d9ef">do</span> ffmpeg -v error -i <span style="color:#e6db74">&#34;</span>$f<span style="color:#e6db74">&#34;</span> -f null - |&amp; sed -e <span style="color:#e6db74">&#34;1i </span>$f<span style="color:#e6db74">&#34;</span> -e <span style="color:#e6db74">&#39;$a\---&#39;</span> | tee -a error.log; <span style="color:#66d9ef">done</span>; unset IFS; set +f
</span></span></code></pre></div><p>Changing the IFS and disabling filename expansion with <code>set -f</code> allows the command to handle special characters like spaces or question marks in filenames. It will print out errors in the terminal and also write them in <code>error.log</code>.</p>
<p>FFmpeg might find problems that do not affect the actual stream in your files:</p>
<pre tabindex="0"><code>   ./OST/Jujutsu Kaisen/01. LOST IN PARADISE (feat. AKLO).flac
=&gt; [flac @ 0x5579ca9e51c0] Could not read mimetype from an attached picture.
   ---
   ./Tut Tut Child - You Hide (feat. Augustus Ghost).flac
   [mjpeg @ 0x5a6c0c161e40] No JPEG data found in image
   [mjpeg @ 0x5a6c0c1650c0] No JPEG data found in image
=&gt; Error while decoding stream #0:1: Invalid data found when processing input.
</code></pre><p><code>stream #0:1</code> is the cover file, while <code>stream #0:0</code> would be the audio stream. Up to you if you want to deal with them. I personally don&rsquo;t, since they doesn&rsquo;t affect playback.</p>
<p>You can guestimate the time the check will take by taking a look at the read throughput on your drive by using <code>iotop</code> or <code>iostat</code> for example.</p>
]]></content:encoded>
    </item>
    <item>
      <title>About me</title>
      <link>https://blog.ampho.fr/about/</link>
      <pubDate>Thu, 18 Sep 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ampho.fr/about/</guid>
      <description>&lt;p&gt;Hi there, my name is Anh Minh. I&amp;rsquo;m a tech enthusiast, basshead, IT engineer based in Paris. I currently manage vSphere virtualization infrastructures on Cisco UCS and Dell hardware. I love all things FOSS, self-hosted, drum &amp;amp; bass, dubstep and the likes.&lt;/p&gt;
&lt;p&gt;In case you&amp;rsquo;re curious, here&amp;rsquo;s what I use to run my home server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Minisforum B550
&lt;ul&gt;
&lt;li&gt;AMD Ryzen 7 5700G&lt;/li&gt;
&lt;li&gt;32 GB or RAM&lt;/li&gt;
&lt;li&gt;&lt;del&gt;GTX 1080&lt;/del&gt; (slowly dying) RTX 2080 Ti&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GL.iNet Flint2 (2.5G links to my ISP and server)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Want to reach out? Feel free to send me a DM on Discord &lt;em&gt;at&lt;/em&gt; Miminepho or an email, contact &lt;em&gt;at&lt;/em&gt; ampho.fr.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Hi there, my name is Anh Minh. I&rsquo;m a tech enthusiast, basshead, IT engineer based in Paris. I currently manage vSphere virtualization infrastructures on Cisco UCS and Dell hardware. I love all things FOSS, self-hosted, drum &amp; bass, dubstep and the likes.</p>
<p>In case you&rsquo;re curious, here&rsquo;s what I use to run my home server:</p>
<ul>
<li>Minisforum B550
<ul>
<li>AMD Ryzen 7 5700G</li>
<li>32 GB or RAM</li>
<li><del>GTX 1080</del> (slowly dying) RTX 2080 Ti</li>
</ul>
</li>
<li>GL.iNet Flint2 (2.5G links to my ISP and server)</li>
</ul>
<p>Want to reach out? Feel free to send me a DM on Discord <em>at</em> Miminepho or an email, contact <em>at</em> ampho.fr.</p>
<p><strong>Disclaimer</strong>: I might get things wrong. Please take everything I write with a grain of salt.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
