{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "nico://log · notes in English",
  "home_page_url": "https://www.nico.com.ar",
  "feed_url": "https://www.nico.com.ar/en/notes/feed.json",
  "description": "English notes and translations from nico://log.",
  "items": [
    {
      "id": "https://www.nico.com.ar/en/one-command-to-back-up-the-whole-homelab/",
      "url": "https://www.nico.com.ar/en/one-command-to-back-up-the-whole-homelab/",
      "title": "One command to back up the whole homelab",
      "content_html": "<p>For quite a while, creating a complete backup of my homelab meant following several separate procedures.</p>\n<p>There are hosts, virtual machines, containers and a few services that require their own specific steps. In some cases a standby instance needs to be synchronized first. In others, a service has to be stopped temporarily before creating the backup, validating it and finally sending it to offsite storage.</p>\n<p>It worked, but there was an obvious problem: too much of the process depended on remembering what had to be done, in which order, and what state everything was in before starting.</p>\n<p>The idea eventually became quite simple: turn all of that into a single operation.</p>\n<pre><code class=\"language-bash\">datacenter-backup.sh run\n</code></pre>\n<p>The command starts a systemd service that coordinates the complete process. This means the backup does not depend on my SSH session remaining open and can continue even after I close the terminal.</p>\n<h2>Different systems, one process</h2>\n<p>Not every backup can be handled in the same way.</p>\n<p>Some machines can be copied while they are running. Others need to be stopped for a few minutes. There are also services where it makes sense to synchronize a standby instance before creating its backup.</p>\n<p>Instead of trying to force a single method onto everything, the orchestrator delegates each part of the process to small specialized scripts.</p>\n<p>At a high level, the flow looks like this:</p>\n<pre><code class=\"language-text\">orchestrator\n    |\n    +-- host configuration\n    +-- remote Linux host\n    +-- virtual machines\n    +-- containers\n    |\n    +-- local validation\n    |\n    +-- offsite upload\n    |\n    +-- remote verification\n</code></pre>\n<p>The main script coordinates these stages and stops the process if one of them fails.</p>\n<h2>Verify before and after</h2>\n<p>One thing I wanted to avoid was considering a backup complete simply because a command finished without errors.</p>\n<p>The files are validated locally after they are created. Compressed archives are checked to make sure they can be read correctly, and checksums are generated for the relevant backups.</p>\n<p>Then comes the second part: offsite storage.</p>\n<p>The backups are uploaded to Dropbox through a container dedicated exclusively to that task. Once each transfer finishes, the local and remote files are compared again using their size and Dropbox content hash.</p>\n<p>So the process does not end with:</p>\n<pre><code class=\"language-text\">upload successful\n</code></pre>\n<p>It ends with something closer to:</p>\n<pre><code class=\"language-text\">backup created\nbackup validated locally\nbackup uploaded\nremote copy verified\n</code></pre>\n<h2>Returning everything to its original state</h2>\n<p>I also wanted running a backup to avoid permanently changing the state of the homelab.</p>\n<p>Before acting on a machine or service, the process records whether it was running or stopped.</p>\n<p>If something has to be started temporarily to perform a backup, it is stopped again afterward. If a running machine needs to be shut down for its backup, it is started again once the operation is complete.</p>\n<p>The same idea applies to supporting services that are only required during part of the process.</p>\n<p>The result is that the backup can temporarily modify the infrastructure, but everything should return to the same operational state once the run is finished.</p>\n<h2>One manifest per run</h2>\n<p>Each execution also creates a manifest containing the files that belong to that backup generation.</p>\n<p>This provides a clear reference for what was produced and what was sent to remote storage during a particular run.</p>\n<p>It does not replace checksums or file verification, but it helps treat each backup run as a complete unit instead of a collection of unrelated files.</p>\n<h2>The first complete run</h2>\n<p>The first real end-to-end run took a little over five hours.</p>\n<p>During that time, the different local backups were created, validated, transferred to offsite storage and verified again on the remote side.</p>\n<p>When the process finished, the orchestrator reported success and the machines, containers and services involved had returned to their expected state.</p>\n<p>The main improvement is not that backups are now faster. They still take several hours and move a considerable amount of data.</p>\n<p>The difference is that the procedure is now defined, reproducible, and has a clear beginning and end.</p>\n<p>Before, I had several backup procedures.</p>\n<p>Now I have one.</p>",
      "summary": "How I turned several manual backup procedures into a single coordinated operation with local validation and offsite verification.",
      "date_published": "2026-09-18T00:00:00+00:00",
      "tags": [
        "homelab",
        "backups",
        "linux",
        "automation"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/when-a-tool-stops-being-experimental/",
      "url": "https://www.nico.com.ar/en/when-a-tool-stops-being-experimental/",
      "title": "When a Tool Stops Being Experimental",
      "content_html": "<p>A few weeks ago a concrete problem showed up at work: we had to publish and take down a banner in dozens of markets, at a specific time, inside Drupal.\nDoing it manually was possible. It was also exactly the kind of task that makes you ask: <strong>why are we doing this by hand?</strong> That is how Banner Scheduler was born.\nThe first version had a pretty narrow goal: schedule visibility changes. Pick the markets, define a date, and let the tool do the work when the time came. But a more interesting problem showed up quickly: visibility was only part of the story.\nWhat happens when the banner content changes too? What do we do with different translations? How do we schedule several changes during a campaign? How do we keep Drupal from becoming the place where the content lives again? What happens if the site is temporarily under maintenance when it is time to run a change?</p>\n<p>That is where the project started to change. Today the Scheduler works together with <strong>Atlas</strong>, another internal tool we use as the source of truth for content and translations. The split ended up pretty clear:</p>\n<ul>\n<li>Atlas knows <strong>what content should exist</strong>.</li>\n<li>The Scheduler knows <strong>when it has to change and when it should be visible</strong>.</li>\n<li>Drupal stays the final destination, not the place where the whole process is managed.</li>\n</ul>\n<p>A campaign can be loaded from a spreadsheet, validate its markets and schedules, prepare the different content changes, and generate the jobs that will run later. There is also a small sequence I really liked: <strong>check, apply, arm</strong>. First check that what we want to do makes sense. Then create the campaign. And only then arm the jobs that can touch production.</p>\n<p>The schedules for the different markets are normalized, the actions are logged, and the worker takes care of executing what was scheduled. If certain temporary conditions prevent a change from happening, there are retries instead of immediately assuming everything failed. All of that ended up paired with an interface where you can see campaigns, upcoming changes, recent activity, and the state of each operation.</p>\n<p>This week the small milestone that justified writing all this happened: <strong>we launched the first real campaign.</strong>\nIt was no longer a test, a dry run, or specially prepared data for building the tool. There was real content, real schedules, and real changes that actually had to happen.</p>\n<p>And it worked. Maybe what I like most about this project is that it did not start from a giant specification or the idea of building a “platform”.\nIt started by looking at a pretty tedious task and thinking: <strong>this could be done better.</strong> The rest appeared question by question. And there are probably still quite a few questions left.</p>",
      "summary": "How a banner-visibility automation grew into a small campaign system coordinating content, markets, schedules, and contingencies.",
      "date_published": "2026-08-28T00:00:00+00:00",
      "tags": [
        "automation",
        "drupal",
        "devops",
        "ai"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/redundant-internet-on-a-budget/",
      "url": "https://www.nico.com.ar/en/redundant-internet-on-a-budget/",
      "title": "Redundant Internet on a Budget",
      "content_html": "<p>The other day I started thinking about what would happen to my home setup if the fiber went down. If the home ISP disappeared for a few hours, I still wanted to be able to get in remotely to the servers, keep some Cloudflare tunnels alive, and have an emergency exit.</p>\n<p>That is how KernelPanic-Rescue was born, a very handmade 4G fallback, built from hardware I already had lying around and a tiny LTE router that looks like a USB stick.</p>\n<p>The main rule was that fiber would always remain the normal connection. The servers run independent checks against the internet and, if most of them fail, they wait another 15 seconds before making any decision.</p>\n<p>If the internet comes back during that window, nothing happens.</p>\n<p>If it is still down, they bring up the emergency connection, change the necessary routes, and some services start going out over 4G. When the fiber recovers in a stable way, everything returns automatically to its original state.</p>\n<p>Those 15 seconds ended up mattering more than it seemed. Early one morning the ISP renewed the public IP and caused a small outage. One of the servers detected it, waited out the confirmation window, and logged:</p>\n<p><code>Fiber ISP health-check failed; confirming in 15 seconds.</code>\n<code>Fiber ISP recovered during confirmation window.</code></p>\n<p>It never switched to 4G. That was exactly what I wanted to avoid: any little blink from the provider triggering a full failover.</p>\n<h2>The Wi-Fi card zoo</h2>\n<p>The least elegant part was figuring out how to connect each machine to the emergency router.</p>\n<p>I tried several USB adapters I had saved: a tiny TP-Link with a Realtek chipset, an old D-Link with a Ralink RT3070, and the built-in Wi-Fi of a Raspberry Pi.</p>\n<p>The theory said any of them should be enough. Reality was different. The TP-Link had what looked like a reasonable signal, but could take full seconds to answer a ping. The old Ralink, on the other hand, ended up working much better. After trying different USB ports, positions, and distances, it was assigned exclusively to Bernarda, one of my Proxmox hosts.</p>\n<p>The spare TP-Link ended up passed through USB to a Kali Linux VM, where it found a much more appropriate second life.</p>\n<p>The LTE router also had something interesting: when connected directly by USB to the server, Linux sees it as a normal Ethernet card. That allowed one of the hosts to skip Wi-Fi entirely and have its own wired connection to the 4G router. It is not exactly datacenter architecture, but it works.</p>\n<h2>What remained</h2>\n<p>The system is still small. I do not want to run streaming or heavy services over 4G. If the fiber goes down, the goal is to keep remote administration, Tailscale, Cloudflare, and a few important things alive until the normal connection returns.</p>\n<p>The experiment ended up being much more entertaining than I expected. It started as “put a Wi-Fi antenna on it in case the internet goes down” and ended up involving policy routing, namespaces, systemd, recycled dongles, and a lot of time moving USB adapters from one port to another.</p>\n<p>When I was a kid, I used to mess around with wire antennas on the terrace at home, trying to catch signals. A few decades later I am basically still doing the same thing. Only now there is Proxmox in the middle.</p>",
      "summary": "A low-budget 4G fallback built from spare hardware and a tiny LTE router, so I can keep remote access, tunnels, and a backup route alive when fiber goes down.",
      "date_published": "2026-08-19T00:00:00+00:00",
      "tags": [
        "linux",
        "proxmox",
        "4g",
        "homelab",
        "tailscale",
        "cloudflare"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/fifteen-years-of-blur-fm/",
      "url": "https://www.nico.com.ar/en/fifteen-years-of-blur-fm/",
      "title": "Fifteen Years of Blur FM",
      "content_html": "<p>Today Blur FM turns fifteen.</p>\n<p>On July 24, 2011, a web radio station started playing that, against all odds, is still on the air. The computers changed, the servers changed, the players changed, the design changed. Almost everything changed, except the core idea.</p>\n<p>Before Blur FM there was Radio deUNA!, in 2009. That one started simply from wanting to have my own radio: the internet let me put together a signal, program music, and broadcast it, and that was enough for me. I still did not really know what I wanted it for. It was a radio because I could make one.</p>\n<p>Blur FM brought what was missing: a purpose. It stopped being “keeping something on the air” and became a place to discover music. No ads, built with independent artists and songs I found around, things that would hardly ever play on a traditional radio station. Indie, ambient, electronic, pop, calm tracks, and others that were kind of hard to classify. Music for working, traveling, and slowing down a bit.</p>\n<p>It was not just about putting together tracks I liked. There was intention in the order, in the selection, in the overall mood. That curation ended up being the heart of the project: someone enters, hits play, and comes across something they were not looking for.</p>\n<p>Blur FM was never a big media operation and never had money behind it. It was always small, independent, and pretty handmade. But it was also my lab: there I learned how to manage servers, stream audio, design interfaces, and keep an identity alive across different eras. And I learned something more important: keeping a project alive for years does not mean working on it all the time. Sometimes it is enough not to turn it off.</p>\n<p>Since 2011, the way we listen to music has changed completely. Streaming arrived, algorithms learned our tastes, and every song is one click away. In that context, a tiny internet radio station sounds a bit anachronistic. But it still has something the platforms do not: not choosing. Entering a place and accepting somebody else's programming. Not knowing what is coming next. Blur FM does not compete with any of that; it is just one possible selection in the middle of the noise.</p>\n<p>Fifteen years do not happen alone. I want to thank Mike Harris, Archie Kennedy, and Dave Gough, from Psand; Diego Carlotta, from Greenlight Media; Denis \"jaromil\" Roio and the dyne.org community; and Nadia Zelnick, the voice of Blur FM and an inseparable part of its identity. Each one contributed something to this story.</p>\n<p>There will probably be new designs, new servers, new ways to listen, seasons of great enthusiasm and others in silence. What matters is that the place is still there: someone comes in, hits play, and finds a song they did not know.</p>\n<p>The radio keeps broadcasting.</p>",
      "summary": "Blur FM turns fifteen on air. The story of a radio that started earlier as Radio deUNA!, but finally found an identity, a purpose, and a reason to keep broadcasting.",
      "date_published": "2026-07-24T00:00:00+00:00",
      "tags": [
        "blur-fm",
        "radio",
        "música",
        "internet",
        "proyectos-personales"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/the-odyssey-of-buying-a-burger/",
      "url": "https://www.nico.com.ar/en/the-odyssey-of-buying-a-burger/",
      "title": "The Odyssey of Buying a Burger",
      "content_html": "<p>Today I went in to buy a burger and ended up in a kind of digital obstacle course, one that seemed designed to measure how much effort I'd put in before giving up and eating somewhere else.</p>\n<p>First I went to the cashier, like always, but they told me I could only pay in cash or with MODO. I never use MODO, though I had the app installed, so I opened it to see if I could sort it out there. Before showing me anything useful, it asked me to update it.</p>\n<p>I didn't feel like updating an app at the register just to buy a burger, so I tried one of the self-service kiosks. In theory those machines are there to speed things up, avoid lines, and let you order at your own pace. In practice, the first thing it did was ask me to log in.</p>\n<p>I just wanted a burger, not to create an account or start a long-term relationship with the company. It took me a while to find the option to skip that step, and it was far less visible than the buttons inviting me to register or sign in.</p>\n<p>Once inside, the menu didn't help much either. The screen was full of giant photos, promotions, combos, and featured products, and the descriptive text was minimal. A lot to look at, not much to actually read: it was hard to tell what each product included or what really set one option apart from another.</p>\n<p>Choosing the burger wasn't the hard part. Ordering the plain version was, because the whole interface, spread across a vertical 32-inch screen less than half a meter from my face, a format already at odds with how we actually see, seemed built to push me toward the bigger, more expensive option. The larger sizes, add-ons, and upgrades got huge photos and flashy buttons. The normal version was there, but you had to find it among all the chances to spend a little more.</p>\n<p>After turning down a pile of extras, I finished the order and got to payment. The kiosk showed two options and I chose Mercado Pago, assuming it would give me a QR code to scan with my phone, like almost anywhere else.</p>\n<p>I stood there in front of the screen, not really sure what to do. After a few seconds an employee came over and explained that it worked the other way around: I wasn't meant to scan a code from the kiosk. I had to open Mercado Pago, generate my own payment QR, and point it at the machine's reader.</p>\n<p>That option wasn't especially obvious inside the app either. I ended up in a section I mostly recognized from paying for public transit, not for buying food. I found the code, pointed it at the reader, and waited.</p>\n<p>It didn't read it.</p>\n<p>We tried again, moving the phone, changing the distance, bringing it closer to the sensor. The reader still wouldn't react, until all at once the kiosk canceled the whole thing and went back to the start screen. The entire order was gone.</p>\n<p>The employee ended up walking me to the next kiosk and typing the burger in from scratch himself. The self-service system had needed a human to finish a purchase that same employee could have rung up at a register in under a minute.</p>\n<p>The thing is, each individual decision probably makes sense on its own. The app wants to build loyalty, the kiosk cuts down on cashiers, the big photos help sell products, the featured options push up the average order. The problem shows up when all those decisions stack on top of each other and nobody seems to be looking at the whole experience from the point of view of the person who just wants to eat something.</p>\n<p>In a few minutes I had to open an app I never use, find out it needed an update, work around the login, navigate an interface built to promote the most expensive options, decode an unusual payment method, and redo the entire order because a reader couldn't recognize a code.</p>\n<p>Self-service is supposed to remove steps and make an everyday task faster. Done badly, it just shifts the company's work onto the customer and turns a simple operation into a test of patience.</p>\n<p>I had gone in to buy a burger. Badly designed technology did everything it could to stop me.</p>",
      "summary": "What should have been a quick purchase ended up involving apps that needed updating, a kiosk designed to push the more expensive options, and such a confusing payment flow that an employee had to place the order from another machine.",
      "date_published": "2026-07-21T00:00:00+00:00",
      "tags": [
        "ux",
        "diseño",
        "interfaces",
        "autogestión",
        "mercado-pago"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/the-migration-that-seemed-hard-and-ended-up-in-an-afternoon/",
      "url": "https://www.nico.com.ar/en/the-migration-that-seemed-hard-and-ended-up-in-an-afternoon/",
      "title": "The Migration That Seemed Hard and Ended Up in an Afternoon",
      "content_html": "<p>On Heroku I had two internal tools set up to solve pretty specific things. They worked, did not give me headaches, and were already part of the day-to-day workflow, but the monthly cost was hard to justify for how small they were.</p>\n<p>The thing is that one of them had grown far more than I had planned at the time. It was no longer just a Node interface in a dyno: it had a PostgreSQL database, import and export processes, a delivery queue, and a worker running separately in another VM, checking every so often whether there was pending work.</p>\n<p>The second one was much simpler. It received a text file, split the content by locale, and returned a ZIP with several HTML files. No database, no persistent storage, yet it was oddly one of the tools that consumed the most hours in the entire account.</p>\n<p>I decided to migrate both to a new Debian VM inside my Proxmox. The idea was to use Docker so each application would stay isolated, with its own dependencies and its own container, all nicely organized. I was not trying to build a giant platform, just leave a tidy host where several tools could coexist without updating Node or adding a new dependency breaking something in another app.</p>\n<p>Before touching anything I confirmed the most basic thing: that the code I had locally was exactly the same as what was running in production. I compared the local commits against the Heroku remote repos for both apps. It sounds obvious, but it saves you from migrating an old version thinking it is the current one.</p>\n<p>The first app needed more care because it had real data in PostgreSQL. I took a logical backup of the database, copied the project and variables to the new VM, brought up PostgreSQL 17 in Docker, and restored the entire dump. Then I validated the tables, counted rows, and checked that the Prisma migrations were up to date before starting the app.</p>\n<p>When the portal booted for the first time, I ran into one of those problems that sounds scarier than it is. The interface loaded, accepted the password, but immediately kicked me back to the login screen. It turned out the app was running in production mode and setting the cookie as secure, while I was reaching it through the local IP over HTTP.</p>\n<p>To test it inside the network I left it in development mode for a while. Then I set up Cloudflare Tunnel, gave it an HTTPS domain, and switched it back to production. The same cookie that would not work over the local IP started behaving fine without me touching the app logic.</p>\n<p>The worker also needed to know where the new portal lived. Until then it was still pointing at the Heroku URL, so I stopped the timer for a bit, updated the environment variable, and confirmed it connected properly to the new domain. When I re-enabled it, it went back to checking the queue normally from its VM.</p>\n<p>The second tool was much more straightforward. No database, no secrets, no external processes. I copied the repo, built the Node image, started the container, and tested the full flow from the terminal: upload a test file, receive the ZIP, and check that the HTML came out correctly.</p>\n<p>Then I moved its existing domain to the same Cloudflare Tunnel. For the person using it nothing changed: the URL stayed the same and the app kept working exactly as before, only now it ran in a container on the new VM instead of on Heroku.</p>\n<p>Once I confirmed both responded properly over HTTPS and that the main flows worked, I deleted the Heroku apps and their resources. The part I was most nervous about ended up being the most mechanical: confirm versions, copy, bring up in parallel, test, and only then cut over.</p>\n<p>I think it went smoothly because I never tried to solve everything at once. Instead of thinking of it as a giant infrastructure move, I broke it into small checkpoints where I could verify each step before continuing.</p>\n<p>Docker also made it very clear what each app needed. One uses Node and PostgreSQL, the other only Node. Cloudflare Tunnel publishes both without opening ports on the router, and the VM ended up as a general host for small tools, not as an artisanal setup impossible to maintain.</p>\n<p>What started as a way to lower a cost ended up organizing the architecture quite a bit. The apps stay separate, the domains did not change, the worker keeps doing its thing, and the infrastructure is ready to add other tools later on.</p>\n<p>The strangest part is that for a long time I imagined this migration as something complicated and delicate. When I finally sat down to do it, both apps were already running on the new VM the same afternoon.</p>",
      "summary": "Two internal tools, a PostgreSQL database, a worker running in another VM, and several domains looked like they were setting up a complicated move. In the end, breaking the work into small steps made everything much easier than expected.",
      "date_published": "2026-07-16T00:00:00+00:00",
      "tags": [
        "heroku",
        "docker",
        "proxmox",
        "nodejs",
        "postgresql",
        "cloudflare",
        "linux",
        "self-hosting"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/a-second-life-for-an-old-pc-with-proxmox/",
      "url": "https://www.nico.com.ar/en/a-second-life-for-an-old-pc-with-proxmox/",
      "title": "A Second Life for an Old PC with Proxmox",
      "content_html": "<p>I had an old PC gathering dust in a box in a corner of my room. For years it was my main machine: an AMD FX 8300, an ASUS motherboard, 16 GB of RAM, a Gigabyte 2 GB graphics card, and a pretty decent power supply. At the time it ran Windows 10, I used it hard for years, and it always responded well.</p>\n<p>After I built my current PC, installed Fedora, and got used to working in Linux every day, the old machine fell off the map. It was not broken, not useless, it had just lost its place.</p>\n<p>The other day I pulled it out of the box, cleaned it, changed the thermal paste, powered it on, and it booted perfectly. It took memory, showed video, and was left waiting for the one thing it still did not have: a disk to boot from.</p>\n<p>That is when the idea came to give it a second life, but not as a main PC or as a replacement for any current server. The idea is much simpler: turn it into a Proxmox lab to power on whenever I want to test something, break a VM, install a new distro, or practice infrastructure without touching anything important.</p>\n<p>I like that separation. My production server has to be boring and stable. I do not want to use it for weird experiments or for trying things that might break. For that it makes much more sense to have a separate machine, old but still capable, where making mistakes is not a problem.</p>\n<p>The hardware is not modern, but it is pretty fun as a lab. A 960 GB SATA SSD for Proxmox and the main VMs, a 1 TB WD Blue disk for test backups or secondary storage, and an old laptop mechanical drive to experiment on without fear. It even has a DVD burner, which is completely unnecessary in 2026, but precisely for that reason kind of charming in a home lab.</p>\n<p>The only less polished part is the network. I do not have direct Ethernet in that room, so the plan is to use a WiFi repeater with a Gigabit Ethernet port. The repeater connects to the home WiFi and the PC sees it as a normal wired connection. For Proxmox that is much cleaner than relying on a direct WiFi card inside the host.</p>\n<p>I also want to try Wake on LAN. The machine has already worked before with a magic packet, so the idea is to be able to turn it on remotely from a Raspberry Pi that stays on all the time with Tailscale. In practice it would be pretty simple: I connect from outside to the home network, send the magic packet, the PC boots, I get into the Proxmox panel, and I start the VM I need.</p>\n<p>What excites me most is not raw power, but the kind of things I can learn there. I want to try Fedora Server, openSUSE, Arch Linux, Omarchy, plain Debian, and some distros that were left out on smaller machines. I would also like to play with Proxmox Backup Server, practice snapshots, do real restores, and understand better how to organize VM templates.</p>\n<p>Another idea that interests me is trying K3s, a lightweight version of Kubernetes. Not because I want to turn my house into a datacenter, but because as a designer and UX engineer it helps to understand better what happens after an interface stops being a design or a prototype.</p>\n<p>There is a path that often stays a bit invisible from the design side: frontend, build, container, deploy, service, ingress, logs, rollback. Being able to walk that flow on a small scale, with a simple app or an internal dashboard, seems like a very concrete way to learn. I do not need huge infrastructure. I need a place where I can test things, see what happens, and roll back without fear.</p>\n<p>The machine can also help me organize my home ecosystem better. Today I have a main PC for work, a server that keeps real services running, a Raspberry Pi for staging and remote access, another one for lightweight static sites, and a few smaller machines for tests. What was missing was a clear place to break things without mixing them with production.</p>\n<p>That would be this PC's role: on-demand lab. After that will come the distros, Kubernetes, the old disks, the templates, and all the other ideas.</p>\n<p>I like it because I am not buying a new PC or complicating something that already works. I am recovering hardware I already had and giving it a much clearer role than before. The old PC does not come back as a main machine, but as an all-terrain Linux lab.</p>",
      "summary": "An old PC gathering dust turned into the perfect excuse to build a Proxmox lab: try distros, understand virtualization better, play with old disks, and keep production separate from experiments.",
      "date_published": "2026-05-27T00:00:00+00:00",
      "tags": [
        "proxmox",
        "home-lab",
        "linux",
        "virtualization",
        "fedora",
        "tailscale",
        "kubernetes",
        "low-tech"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/aires-pulse-and-a-transaction-between-pi-zeros/",
      "url": "https://www.nico.com.ar/en/aires-pulse-and-a-transaction-between-pi-zeros/",
      "title": "Aires Pulse and a Transaction Between Pi Zeros",
      "content_html": "<p>The other day I had an idea in my head: connect two worlds that already existed, the Raspberry Pi Zero 2 Ws and my little personal sites. The idea was for one Pi Zero to generate information and the other to publish it. A kind of transaction between Pi Zeros.</p>\n<p>The machine that generates the data is <strong>Pipeta</strong>, more lab than server. The one that publishes is <strong>Pipita</strong>, the Zero already serving <a href=\"https://www.nico.com.ar\" title=\"nico.com.ar\">nico.com.ar</a>, <a href=\"https://run.nico.ar\" title=\"run.nico.ar\" target=\"_blank\" rel=\"noopener noreferrer\">run.nico.ar</a>, and <a href=\"https://zero.nico.ar\" title=\"zero.nico.ar\" target=\"_blank\" rel=\"noopener noreferrer\">zero.nico.ar</a> with NGINX. In between: just <code>rsync</code>, static JSON files, and a little patience.</p>\n<p>That is how <strong><a href=\"https://aires.nico.ar\" title=\"Aires Pulse\" target=\"_blank\" rel=\"noopener noreferrer\">Aires Pulse</a></strong> started.</p>\n<p>At first it was just a dark page with a nice name and a promise: show signals from Buenos Aires in the air, the river, and the weather. Pipeta already had a previous experiment for looking at planes near Buenos Aires, so the most natural thing was to turn that information into an <code>aircraft.json</code> and show it on the site. First as numbers: how many planes, countries, speed, altitude. Then the map appeared.</p>\n<p>With Leaflet, the site went from a card full of data to a small visual radar. The points over Buenos Aires, the Río de la Plata, the planes coming and going. All of a sudden that lab-ish idea felt like a tiny home monitoring console.</p>\n<p>The second layer was weather. Pipeta started generating a <code>weather.json</code> with current conditions and a suggestion for when to run. It was not just about showing temperature: the point was to turn that data into something useful for deciding when to head out.</p>\n<p>At first that information lived in <a href=\"https://aires.nico.ar\" title=\"Aires Pulse\" target=\"_blank\" rel=\"noopener noreferrer\">Aires Pulse</a>. But then an obvious question showed up: if the recommendation is about running, why not use it in <a href=\"https://run.nico.ar\" title=\"run.nico.ar\" target=\"_blank\" rel=\"noopener noreferrer\">run.nico.ar</a> too?</p>\n<p>That is when the experiment got more interesting.</p>\n<p><a href=\"https://run.nico.ar\" title=\"run.nico.ar\" target=\"_blank\" rel=\"noopener noreferrer\">run.nico.ar</a> is generated from <strong>Pipa</strong>, the Raspberry Pi 5. It is a static site that updates when I publish an activity or when its daily routine runs. But the weather data was coming from Pipeta and the site was hosted on Pipita. So there were three different machines, each with its own role, and the data had to travel between them coherently.</p>\n<p>The solution was to separate the static build from the live data. <a href=\"https://run.nico.ar\" title=\"run.nico.ar\" target=\"_blank\" rel=\"noopener noreferrer\">run.nico.ar</a> still lives in <code>/srv/data/www/run.nico.ar</code>, but the JSON with running conditions is published outside it, in a <code>live</code> directory, and NGINX serves it as if it were <code>/data/running-conditions.json</code>. That way, Pipa can deploy with <code>rsync --delete</code> without overwriting the data that Pipeta updates every few minutes.</p>\n<p>Curiously, the hardest part was not the technical one, but the product one.</p>\n<p>I had to decide what to show, how to show it, and where. The running-conditions block went through several forms before the card started answering one concrete question: <strong>what is the best time to run?</strong> Once the answer became easy to read, the design stopped getting in the way.</p>\n<p>In parallel, <a href=\"https://run.nico.ar\" title=\"run.nico.ar\" target=\"_blank\" rel=\"noopener noreferrer\">run.nico.ar</a> also got a split between the editorial \"Next run\" and the practical \"Workout\": one explains what makes sense to do and why, the other turns it into blocks that can be loaded manually into Garmin Connect. It is not a real integration. It is something simpler and more honest: a clear guide.</p>\n<p>In the end, what was interesting was not just that <a href=\"https://aires.nico.ar\" title=\"Aires Pulse\" target=\"_blank\" rel=\"noopener noreferrer\">Aires Pulse</a> came online, or that <a href=\"https://run.nico.ar\" title=\"run.nico.ar\" target=\"_blank\" rel=\"noopener noreferrer\">run.nico.ar</a> could consume live data. What was interesting was seeing how several very small machines ended up with well-defined roles.</p>\n<p>Pipeta observes. Pipita publishes. Pipa interprets.</p>\n<p>All with very basic tools: NGINX, JSON, cron, <code>rsync</code>, and a bit of Python. I like that scale. It is not big infrastructure, and it does not need to be. It is just a personal ecosystem where each piece does one simple thing and does it pretty well.</p>\n<p>And that, for now, is enough.</p>",
      "summary": "What started as a slightly absurd idea - making one Pi generate signals and another publish them - turned into Aires Pulse: a small Buenos Aires home radar that also started feeding run.nico.ar with real running conditions.",
      "date_published": "2026-05-04T00:00:00+00:00",
      "tags": [
        "raspberry-pi-zero-2w",
        "aires-pulse",
        "run-nico-ar",
        "nginx",
        "json",
        "home-lab",
        "low-tech"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/this-website-moved-to-alpine/",
      "url": "https://www.nico.com.ar/en/this-website-moved-to-alpine/",
      "title": "This Website Moved to Alpine",
      "content_html": "<p>A few days ago I wrote about <strong>Pipeta</strong>, a Raspberry Pi Zero 2 W with Alpine Linux meant to be a pure sandbox. The idea was to experiment without pressure, without production, and without turning every test into infrastructure.</p>\n<p>What I did not expect was that the same experience would end up pushing another, more concrete decision: migrating the other Zero 2 W as well. That is where <code>nico.com.ar</code>, <code>run.nico.ar</code>, and <code>zero.nico.ar</code> live, all served with NGINX behind Cloudflare Tunnel.</p>\n<p>The reason for the change was the solid behavior Alpine Linux had already shown. While Pipeta, on Alpine, was behaving impeccably, Pipita was still on 32-bit Raspberry Pi OS Lite and would sometimes drift into weird states where it looked connected to WiFi but in practice had gone half zombie. It did not happen all the time, but enough to stop feeling reliable.</p>\n<p>So I did what had to be done: backed up the sites, inventoried the config, cleaned up everything I had already offloaded to other machines, and reinstalled from scratch.</p>\n<p>Curiously, the hardest part was not bringing <strong>NGINX</strong> back up, or restoring <strong>Cloudflare Tunnel</strong>, or getting the deploy flow via <code>rsync</code> working again. The most annoying bit was something much more basic: the damn <strong>headless WiFi</strong>, which would later let me get SSH access and do the setup.</p>\n<p>Once that was fixed, the rest came together very quickly: new user, SSH, restored sites, active tunnel, deploys working again, and everything serving as before.</p>\n<p>In the end, the result was very good. Not so much because I had “migrated infrastructure,” but because I had put this machine on a much more coherent base for what it does: 64-bit Alpine Linux, 512 MB of RAM, NGINX, and an outbound Cloudflare tunnel.</p>\n<p>There was also something interesting about seeing how a simple lab-machine experiment ended up improving another one that actually does real work. Sometimes a sandbox does not just help you learn: it also clears the path for what comes next.</p>\n<p>In this case, at least for now, it seems like it was worth it.</p>",
      "summary": "After seeing how well Pipeta behaved with Alpine Linux, I ended up migrating Pipita too, the Raspberry Pi Zero 2 W that serves nico.com.ar, run.nico.ar, and zero.nico.ar. The surprise was that the hardest part was not restoring NGINX or the tunnel, but sorting out headless WiFi.",
      "date_published": "2026-04-15T00:00:00+00:00",
      "tags": [
        "alpine-linux",
        "raspberry-pi-zero-2w",
        "nginx",
        "cloudflare-tunnel",
        "low-tech"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/pipeta-alpine-and-going-back-to-basics/",
      "url": "https://www.nico.com.ar/en/pipeta-alpine-and-going-back-to-basics/",
      "title": "Pipeta, Alpine, and the Charm of Going Back to Basics",
      "content_html": "<p>These days I found myself in an interesting situation: having a Raspberry Pi Zero 2 W turned on in Buenos Aires, running Alpine Linux, while I am far from home and do not need anything urgent from that machine. In other words, the ideal context to start trying things without guilt.</p>\n<p>The machine is called <strong>Pipeta</strong> and the name fits, because that is exactly how I want to use it: as a pure sandbox to experiment, learn, and make mistakes without turning every attempt into a service, an obligation, or another piece of infrastructure to maintain.</p>\n<figure class=\"note-photo\">\n  <img src=\"/assets/img/notes/pipeta-alpine-linux-20260407.webp\" alt=\"Pipeta, a Raspberry Pi Zero 2 W turned on over a dark surface\" loading=\"lazy\" />\n  <figcaption>Pipeta, ready to keep doing small, manual, and pretty calm experiments.</figcaption>\n</figure>\n\n<h3>The charm of not doing something important</h3>\n<p>A lot of the time we learn technology by trying to solve something big too quickly. Stand up a server, expose a subdomain, automate half the planet, or turn a script into a system before its time. This time I wanted the opposite.</p>\n<p>The rule was pretty simple: no production, no availability, and no leaving things running just because. Only small, manual, local things that were harmless enough to play with without pressure.</p>\n<h3>The first experiments</h3>\n<p>I started with two very simple Python scripts. One to compare weather between cities and another to read headlines from RSS feeds. They were not especially sophisticated tools, but that was exactly the point.</p>\n<p>What was interesting was understanding more clearly what each layer does: Python as the language, <code>requests</code> to fetch data, <code>feedparser</code> to read RSS, a weather API, and then a bit of my own logic to organize everything and present it more clearly. There was no strange magic behind it, just data, simple rules, and some judgment to show it well.</p>\n<p>Saying it like that makes it sound obvious, but seeing it work in a remote terminal, on such a tiny machine, had a lot of charm.</p>\n<h3>From script to little program</h3>\n<p>Then came the next step, which was to put those pieces together into a sort of tiny interactive lab. That is how <strong>Pipeta Lab</strong> appeared, a terminal menu script that lets me compare cities, read headlines, and combine both things in a simple observatory-style view.</p>\n<p>It is still small, manual, and very light. And that is exactly what interested me the most: not building an app, a dashboard, or a platform, but a little program that feels alive enough to help me understand better how the pieces fit together.</p>\n<h3>When SQLite showed up</h3>\n<p>The most interesting jump so far was adding <strong>SQLite</strong>. Not because I needed it urgently, but because it seemed like a good way to touch a database without bringing in too much complexity.</p>\n<p>Until then, data appeared on screen and disappeared there. With SQLite something else showed up: persistence. The program no longer just queried and displayed, it could also save one run and read it back later. That made a structure that is always there, even if you do not always see it, much clearer: input, processing, persistence, and output.</p>\n<p>In other words, the script stopped being only a live query and started looking a bit more like a system, even if it was still a very small experiment.</p>\n<h3>What I liked most</h3>\n<p>I think the best part of this little adventure was not Alpine, or Python, or SQLite separately. The best part was going back to something very basic and very healthy: learning without the pressure to publish, automate, or turn everything into “infrastructure” right away.</p>\n<p>Open a terminal, write something, break an indentation, wrestle with a capricious API, understand why a city is missing, and save three rows in a database may sound tiny, but that scale is exactly where it becomes easier to understand what is going on.</p>\n<h3>What may come next</h3>\n<p>For now I want to keep Pipeta in that place: pure sandbox, lab, and test box. Maybe later one of these little things moves to another machine or ends up online, but I would rather have that happen later, if it makes sense, and not as the starting point.</p>\n<p>It is good to remember that not every project has to be born as infrastructure. Sometimes a tiny idea, a terminal, and some free time are enough to learn a bit more.</p>",
      "summary": "These days I started using Pipeta, a Raspberry Pi Zero 2 W meant purely as an experimentation playground. The idea was to use it to learn without pressure, with small scripts, weather, RSS, SQLite, and the sort of minimal tests that do not change the world but teach a lot.",
      "date_published": "2026-04-05T00:00:00+00:00",
      "tags": [
        "alpine-linux",
        "raspberry-pi-zero-2w",
        "python",
        "sqlite",
        "rss",
        "weather",
        "argensonix-labs"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/nico-run-a-way-to-read-my-workouts/",
      "url": "https://www.nico.com.ar/en/nico-run-a-way-to-read-my-workouts/",
      "title": "nico://run: A Way to Read My Workouts",
      "content_html": "<p>After <strong>Pacer</strong>, the next need showed up pretty quickly. I did not just want better data for ChatGPT; I also wanted a place of my own to read what I’ve been doing more clearly. I was not looking for a running app, or another widget-filled dashboard, or a homemade copy of Strava. What I wanted was simpler: to see the last session, understand how the week is going, have an idea of what comes next, and read all that with a bit more clarity. That is how <strong>nico://run</strong> was born.</p>\n<h3>What was missing</h3>\n<p>Pacer solved the data-prep part pretty well. It pulls activities from Strava, summarizes them, builds a cleaner brief, and lets me have a better conversation with ChatGPT. But there was still an intermediate layer missing, because having the data and actually seeing it arranged with some intention are two different things. That is where the idea of a personal running portal started to take shape: one that would not try to record everything, but instead help me interpret the important stuff a little better.</p>\n<h3>What nico://run is</h3>\n<p><strong>nico://run</strong> is basically a reading layer on top of Pacer. It lives at <a href=\"https://run.nico.ar/\" target=\"_blank\" rel=\"noopener noreferrer\">run.nico.ar</a>, and what it does is take the snapshot I already prepare on the Pacer side and turn it into something more legible: the latest run as the main block, the next workout as a short suggestion, a weekly summary, and a few links to keep reading interesting things about running and wellness. The logic is pretty simple at heart: <strong>Pacer prepares, nico://run organizes, and ChatGPT interprets</strong>. Each piece does one distinct job, and for now that has worked better than trying to cram everything into one place.</p>\n<h3>How it runs</h3>\n<p>The idea is still to keep it light. It is built as a static site, works with local data, and avoids unnecessary complexity. It is also meant to run comfortably even on small hardware, which is a big part of the project’s appeal. I really like the idea of showing something useful without needing oversized infrastructure to solve a fairly simple problem.</p>\n<h3>What it already does</h3>\n<p>Today <strong>nico://run</strong> already does something concrete for me. It lets me look at the last session without digging through a dozen screens, quickly grasp the context of the week, and keep a friendlier layer between the raw workout and the next day’s decision. It does not replace Garmin or Strava, and it does not try to. It sits in that odd space between the watch and the next workout.</p>\n<h3>What could come next</h3>\n<p>There is still room to keep polishing it. I would like to improve the history archive, give some views more personality, build a cleaner layer for the race calendar, and find better ways to connect the base plan with what I actually end up doing. But the important part is already there: <strong>nico://run stopped being a nice idea and started turning into a tool of its own.</strong></p>",
      "summary": "After building Pacer, I took one more step and launched nico://run: a lightweight portal that takes that data, organizes it better, and turns it into a clearer reading of the last session, the week, and what might come next.",
      "date_published": "2026-03-16T00:00:00+00:00",
      "tags": [
        "nico-run",
        "pacer",
        "running",
        "astro",
        "raspberry-pi",
        "argensonix-labs"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/pacer-from-screenshots-to-a-clean-chatgpt-brief/",
      "url": "https://www.nico.com.ar/en/pacer-from-screenshots-to-a-clean-chatgpt-brief/",
      "title": "Pacer: From Screenshots to a Clean Brief for ChatGPT",
      "content_html": "<p>Today <strong>Pacer</strong> was born, a small app built for one very specific thing: <strong>stop handing screenshots to ChatGPT and start giving it useful context</strong>.</p>\n<p>The idea came from a very simple need. When I want to review how I’ve been training and decide what to do the next day, I do not need nine screenshots, or a novel explaining everything, or to rely on memory to remember what I did three days ago.</p>\n<p>I need this:</p>\n<ul>\n<li>what I have been doing lately</li>\n<li>how much I ran</li>\n<li>whether I rode the bike</li>\n<li>whether I did strength work</li>\n<li>how I feel today</li>\n</ul>\n<p>And that’s it.</p>\n<h3>The underlying problem</h3>\n<p>For a long time the flow was more manual than anything else.</p>\n<p>Look at Garmin.\nLook at Strava.\nTake screenshots.\nSend them.\nExplain how things feel.\nSummarize again by hand.</p>\n<p>It worked, yes. But there was too much friction.</p>\n<p>What was interesting was that, once I tried to automate it, a few truths showed up pretty quickly:</p>\n<ul>\n<li><strong>Strava</strong> works much better as a data source than as a website to scrape</li>\n<li><strong>Garmin</strong> is still better as a watch and training ecosystem than as an open platform for personal integrations</li>\n<li><strong>Playwright</strong> is good, but it did not make sense to make it the star of something Strava already solved better through an API</li>\n</ul>\n<h3>What Pacer ended up being</h3>\n<p>Pacer became a simple and much saner tool:</p>\n<ul>\n<li>it pulls my latest activities from the <strong>Strava API</strong></li>\n<li>it stores everything in JSON</li>\n<li>it serves a tiny web app</li>\n<li>it automatically summarizes recent load</li>\n<li>it lets me fill in just a few manual fields</li>\n<li>it generates text ready to copy or download</li>\n</ul>\n<p>I then pass that text to ChatGPT and the back-and-forth gets much cleaner.</p>\n<h3>What the app shows</h3>\n<p>For now Pacer already summarizes a few useful things:</p>\n<ul>\n<li>last activity</li>\n<li>last run</li>\n<li>last ride</li>\n<li>summary of the last 7 days</li>\n<li>running volume</li>\n<li>cycling volume</li>\n<li>strength and workout sessions</li>\n</ul>\n<p>And it also has a very short manual block to fill in:</p>\n<ul>\n<li>overall feeling</li>\n<li>legs</li>\n<li>sleep</li>\n<li>extra note</li>\n</ul>\n<p>That alone lowers the friction a lot.</p>\n<h3>The most important part</h3>\n<p>The point was not to make another “fitness” app.</p>\n<p>The point was to build <strong>a bridge between my data and a useful conversation</strong>, to help me answer one concrete question:</p>\n<p><strong>what should I do tomorrow?</strong></p>\n<p>In that sense, Pacer already does its job.</p>\n<h3>What went well</h3>\n<p>A few things ended up fitting together better than expected:</p>\n<ul>\n<li>the Strava fetch works</li>\n<li>the JSON is useful</li>\n<li>copy to clipboard / download txt make a lot more sense than they seemed to</li>\n<li>I no longer depend on screenshots</li>\n</ul>\n<h3>Next step</h3>\n<p>Polish it without making it complicated:</p>\n<ul>\n<li>a more compact brief</li>\n<li>local persistence for the manual fields</li>\n<li>a more polished deploy flow</li>\n</ul>\n<p>But the important part has already happened.</p>\n<p><strong>Pacer stopped being an idea and started being useful.</strong></p>",
      "summary": "I built Pacer, a local app that pulls my activities from Strava, summarizes recent load, and lets me generate a simple brief to pass to ChatGPT without relying on screenshots or huge blocks of text.",
      "date_published": "2026-03-09T00:00:00+00:00",
      "tags": [
        "pacer",
        "strava",
        "running",
        "raspberry-pi",
        "nodejs",
        "automation",
        "argensonix-labs"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/aura-minimal-immersive-experience/",
      "url": "https://www.nico.com.ar/en/aura-minimal-immersive-experience/",
      "title": "Aura: a Minimal, Immersive Experience",
      "content_html": "<p>Today <strong>Aura</strong> went to production, the new standalone player for Blur FM, available at <a href=\"https://play.blurfm.com/\" target=\"_blank\" rel=\"noopener noreferrer\">play.blurfm.com</a>.</p>\n<p>The idea is to stop thinking of the player as “that thing kind of hanging off the site” and start treating it as its own <strong>experience</strong>, closer to a music app than to a traditional page.</p>\n<h3>What got built</h3>\n<ul>\n<li>A new project in its own repo</li>\n<li>Built on <strong>Astro</strong></li>\n<li>Designed as an installable <strong>PWA</strong></li>\n<li>Automatic deploy with <strong>GitHub Actions</strong></li>\n</ul>\n<h3>The good part about splitting it out</h3>\n<p>Blur FM's main site stays the “institutional” website, for lack of a better word.\nAura, on the other hand, starts as a 100% listening-focused experience.</p>\n<p>I like that because it keeps the picture much clearer:</p>\n<ul>\n<li><a href=\"https://www.blurfm.com/\" target=\"_blank\" rel=\"noopener noreferrer\">www.blurfm.com</a> as the site</li>\n<li><a href=\"https://play.blurfm.com/\" target=\"_blank\" rel=\"noopener noreferrer\">play.blurfm.com</a> as the player app</li>\n</ul>\n<p>Later on it will be possible to bring something lighter into the main site, but starting separately feels a lot healthier.</p>\n<h3>What it borrowed from the old project</h3>\n<p>Aura did not come out of nowhere. It borrows visual references from Blur FM's main repo:</p>\n<ul>\n<li>Colors</li>\n<li>Logo</li>\n<li>Typography</li>\n<li>Overall look</li>\n</ul>\n<p>All that without carrying over the old structure. It was important not to mix architectures.</p>\n<h3>The deploy</h3>\n<p>The nice part was setting it up with the same overall approach I had already been using in Blur FM:</p>\n<ul>\n<li>Push to GitHub</li>\n<li>Automatic build</li>\n<li>Deploy via <strong>SSH + rsync</strong></li>\n<li>Debian server configured with Apache</li>\n</ul>\n<h3>MVP with minimum effort</h3>\n<p>The project started as a low-fi mockup in Excalidraw and quickly made it to a real URL. It turned into a product fairly fast.</p>\n<p>It is not fully done, not even close, but it already has the important part: its own base, a clear identity, and deploy sorted out.</p>\n<h3>Next step</h3>\n<p>Polish the player without rushing:</p>\n<ul>\n<li>More robust live metadata</li>\n<li>A better dynamic background</li>\n<li>Recently played panel</li>\n<li>Fine-tuning for mobile and TV</li>\n</ul>\n<p>But the most important thing already happened: <strong>Aura exists.</strong></p>",
      "summary": "A new standalone player for Blur FM, with its own repo, automatic deploy to Apache, and a base ready to grow into a real app.",
      "date_published": "2026-03-07T00:00:00+00:00",
      "tags": [
        "blurfm",
        "astro",
        "github-actions",
        "apache",
        "pwa",
        "radio",
        "automation"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/heroku-hosted-splitter-in-30-minutes/",
      "url": "https://www.nico.com.ar/en/heroku-hosted-splitter-in-30-minutes/",
      "title": "A Heroku-Hosted Splitter in 30 Minutes",
      "content_html": "<p>In two iterations (half an hour, at most) I put together <strong>locale-splitter</strong>: a tiny web tool to trim the master translation TXT and download it as a <strong>ZIP</strong>, already <strong>running on Heroku</strong>.</p>\n<p>The TXT contains concatenated HTML and the splitter cuts it when it finds separators like <code>|- (EN-PH) faq-contact-us</code>. Without that exact format, nothing gets cut or renamed.</p>\n<h3>What the MVP does</h3>\n<ul>\n<li>You upload a <code>.txt</code></li>\n<li>It detects <code>|- (LOCALE) slug</code> markers</li>\n<li>It cuts the content between markers (without including the marker line)</li>\n<li>It generates a ZIP with all the HTML in one folder, with naming:</li>\n<li><code>(EN-US) faq-common-concerns.html</code></li>\n</ul>\n<h3>The crazy part</h3>\n<p>Codex not only generated the app (Node/Express), it also pushed it to GitHub and published it on Heroku. It got carried away creating a new repo at first even though I already had one defined, but with a redeploy pointing to the right repo it was ready.</p>\n<h3>Next step</h3>\n<p>Add a <code>report.txt</code> if any warnings show up (for example, markers with no content).</p>",
      "summary": "A tiny web app that splits a master translation TXT by locale markers and downloads a ZIP with ready-to-use HTML, with consistent naming.",
      "date_published": "2026-03-04T00:00:00+00:00",
      "tags": [
        "ipsos",
        "tools",
        "i18n",
        "locales",
        "automation",
        "heroku"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/blur-fm-fanout-argentina-europe-relays/",
      "url": "https://www.nico.com.ar/en/blur-fm-fanout-argentina-europe-relays/",
      "title": "Blur FM Fanout: Argentina Origin, Europe Relays",
      "content_html": "<p>To make Blur FM work better, the idea was: <strong>four qualities from SAM</strong> (320, 128, 64 and 32 kbps), <strong>Icecast in Argentina as the base</strong>, <strong>a relay in Europe</strong> to share the load, and <strong>Cloudflare</strong> so the public URLs point to the most convenient endpoint (AR or EU).</p>\n<p>In practice this ends up as a <em>fanout</em>: the “mother” Icecast publishes the streams, and one (or more) Icecast instances in Europe <strong>join as a relay</strong> and serve those same qualities locally.</p>\n<h3>What problem it solves</h3>\n<ul>\n<li><strong>Less latency</strong> for listeners in Europe (it starts faster and drops less).</li>\n<li><strong>More stability</strong>: if European traffic falls onto the relay, the source in Argentina gets some breathing room.</li>\n<li><strong>Consistency</strong>: now playing comes from one place and looks the same on both sides.</li>\n</ul>\n<h3>The magic part: streaming “RDS”</h3>\n<p>The best part is that not only the audio travels, but also the <strong>metadata</strong> (title, artist, etc.).\nSo the European relay ends up showing the same “Now Playing” as the source.</p>\n<p>That helps because:</p>\n<ul>\n<li>The web player shows the current track without inventing anything.</li>\n<li>Many apps read that info directly from the stream.</li>\n<li>Cloudflare can send the user to the most convenient endpoint (AR or EU).</li>\n<li>Everything stays consistent and there is no need to maintain parallel systems.</li>\n</ul>\n<h3>Result</h3>\n<p>The streams replicate in Europe, the load is distributed, and the experience improves, while keeping everything consistent. And on top of that, the public URLs are ready to grow in the future (finer routing, separate stats, whatever) without turning the infra into a Frankenstein.</p>",
      "summary": "How I set up a simple Icecast fanout to serve Blur FM in Europe with lower latency while keeping now playing intact.",
      "date_published": "2026-02-28T00:00:00+00:00",
      "tags": [
        "blurfm",
        "icecast",
        "streaming",
        "infra"
      ]
    },
    {
      "id": "https://www.nico.com.ar/en/welcome-to-nico-log/",
      "url": "https://www.nico.com.ar/en/welcome-to-nico-log/",
      "title": "Welcome to nico://log",
      "content_html": "<p>Slackware's site is known in the Linux world for running on a Pentium III, 600 MHz, with 512 megabytes of RAM.</p>\n<p>After a few rounds with GitHub Copilot and Codex, this is my first test for publishing a light note.</p>",
      "summary": "Slackware's site is known in the Linux world for running on a Pentium III, 600 MHz, with 512 megabytes of RAM. After a few rounds with GitHub Copilot and Codex, this is my first test for publishing a light note.",
      "date_published": "2026-02-26T23:36:00-03:00",
      "tags": [
        "infra",
        "low tech"
      ]
    }
  ]
}