<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Migsar Navarro</title>
    <description>The latest articles on DEV Community by Migsar Navarro (@migsarnavarro).</description>
    <link>https://dev.to/migsarnavarro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F148023%2F888f25ad-0730-4a3c-a4cc-ab261bc1e36c.jpg</url>
      <title>DEV Community: Migsar Navarro</title>
      <link>https://dev.to/migsarnavarro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/migsarnavarro"/>
    <language>en</language>
    <item>
      <title>Astro + Decap in 2026</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Tue, 13 Jan 2026 09:20:12 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/astro-decap-in-2026-3mj3</link>
      <guid>https://dev.to/migsarnavarro/astro-decap-in-2026-3mj3</guid>
      <description>&lt;p&gt;This morning I finally started working on a project I had been thinking about for a while, an easy to maintain portfolio blog for photography and architecture.&lt;/p&gt;

&lt;p&gt;I used this opportunity to explore &lt;a href="https://decapcms.org/" rel="noopener noreferrer"&gt;Decap&lt;/a&gt;, which is a git-based CMS that I wanted to try for some time but never took the time to explore. Some years ago I discovered the project while I was thinking in doing something similar.&lt;/p&gt;

&lt;p&gt;I really enjoy working with &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; and one thing I've often wished for when writing for my personal blog is a visual interface that reduces the friction to publish content. I like my site to be static, I don't need and don't want to have a db even if it is managed, but I want to have a nice interface to write my posts which is not an IDE or Obsidian, something a bit more customized for my particular blog.&lt;/p&gt;

&lt;p&gt;It took a little longer than expected to have things working, so I decided to write this post to help others that are thinking about using the same stack.&lt;/p&gt;




&lt;p&gt;From the docs I saw there were two ways of installing Decap, use a CDN version or use a package manager like &lt;code&gt;npm&lt;/code&gt;. I decided I wanted to use the second option, but I felt a bit lost, then what? where to import and call the module?&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick note about auth
&lt;/h2&gt;

&lt;p&gt;I really didn't care about auth at the moment, I just wanted the editor, which is something that makes me believe there is room for another solution in this space, and I didn't wanted to have to create an actual repository for the test, I mean, I do have a local repository, but didn't want to push it to Github or Gitlab.&lt;/p&gt;

&lt;p&gt;So I was looking for the minimal installation even if it took longer to get it right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic structure with Astro
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;config.yml&lt;/code&gt; in &lt;code&gt;/public&lt;/code&gt;. It is needed in public because we are going to use an Astro page for the index, and it will reference files from public root.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# when using the default proxy server port&lt;/span&gt;
&lt;span class="na"&gt;local_backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt;
  &lt;span class="na"&gt;proxy_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8081/api/v1"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
&lt;span class="na"&gt;media_folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/src/assets/images/uploads"&lt;/span&gt;
&lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;posts&lt;/span&gt;
    &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/src/pages/blog&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Posts"&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Title&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;title&lt;/span&gt;
        &lt;span class="na"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;/src/pages/admin.astro&lt;/code&gt; to have &lt;code&gt;/admin&lt;/code&gt; route. This file can be as simple as this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---

---

&amp;lt;html lang="en"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;link rel="icon" type="image/svg+xml" href="/favicon.svg" /&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width" /&amp;gt;
    &amp;lt;meta name="generator" content={Astro.generator} /&amp;gt;
    &amp;lt;title&amp;gt;Astro&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;script&amp;gt;
      import CMS from "decap-cms-app";

      CMS.init();
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;As you can see, the script is pretty much the same code that appears in the docs, but I am giving a bit more context here because I had to spend some time putting the information together, and it can be confusing, at least it was for me. All the information is from the docs and the repositories.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Minimal &lt;code&gt;config.yaml&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;It was try and fix. There is a documentation reference for &lt;a href="https://decapcms.org/docs/configuration-options/" rel="noopener noreferrer"&gt;configuration options&lt;/a&gt; but it lacks a summary, so it is not very useful for getting started.&lt;/p&gt;

&lt;p&gt;Instead of having the &lt;code&gt;config.yml&lt;/code&gt; file in &lt;code&gt;/public&lt;/code&gt; it is possible to add a &lt;code&gt;&amp;lt;link href="/admin/config.yml" type="text/yaml" rel="cms-config-url" /&amp;gt;&lt;/code&gt; tag in the html as described in &lt;a href="https://decapcms.org/docs/configuration-options/" rel="noopener noreferrer"&gt;Configuration options&lt;/a&gt; and &lt;a href="https://docs.astro.build/en/guides/cms/decap-cms/#configuration" rel="noopener noreferrer"&gt;Astro's Decap CMS Guide&lt;/a&gt;. I don't like that option because it spreads configuration across files a little bit more.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Local repository
&lt;/h4&gt;

&lt;p&gt;First I tried to use a different backend and saw the &lt;code&gt;test-repo&lt;/code&gt; option, it is nice to know things are working, but it is totally ephemeral, so it was not a good solution.&lt;/p&gt;

&lt;p&gt;Then I saw the &lt;a href="https://decapcms.org/docs/working-with-a-local-git-repository/" rel="noopener noreferrer"&gt;Working with a local repository&lt;/a&gt; section, which is just what I needed, but it doesn't say anything about the &lt;code&gt;backend&lt;/code&gt; config needed and it was working fine with a &lt;code&gt;config.yml&lt;/code&gt; file but it stopped working when I tried the code configuration (which is not relevant for now other than the fact that I presented the "corrected" yaml file), since it used &lt;code&gt;decap-server&lt;/code&gt; I looked in that repo and found in the &lt;code&gt;README.md&lt;/code&gt; the &lt;code&gt;proxy&lt;/code&gt; configuration I presented here.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. &lt;code&gt;CMS.registerPreviewTemplate&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This mysterious line appears in the &lt;a href="https://decapcms.org/docs/install-decap-cms/" rel="noopener noreferrer"&gt;Install Decap CMS&lt;/a&gt; section and later again in the &lt;a href="https://decapcms.org/docs/manual-initialization/" rel="noopener noreferrer"&gt;Manual initialization&lt;/a&gt;, while it is great to know that the registry is available via the CMS object and it works as expected, that information is not really informative for a first time user.&lt;/p&gt;

&lt;p&gt;Apparently it is not needed in any case because I ignored the purpose until a few minutes ago. It is described in &lt;a href="https://decapcms.org/docs/customization/#registerpreviewtemplate" rel="noopener noreferrer"&gt;Customizing Decap CMS -&amp;gt; Creating custom previews&lt;/a&gt; and it allows you to register a custom template for a collection. The template is a React component that will render the data from the collection.&lt;/p&gt;

&lt;p&gt;This is totally out of the scope of this post, but I think it is relevant if you, like me, don't like to copy code unless you know it is needed: you don't need it at the moment and you can safely skip that line.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Running with local repository
&lt;/h4&gt;

&lt;p&gt;This is something that I don't think is explained in the docs, but probably appears somewhere in the architecture section.&lt;/p&gt;

&lt;p&gt;When running locally you need some local API, the idea behind Decap CMS is to use git, it doesn't make sense to add other types of management since it would require a lot of effort to create and maintain.&lt;/p&gt;

&lt;p&gt;Therefore, when running locally you need to also run a local server at a different port from your dev server and that's what &lt;code&gt;decap-server&lt;/code&gt; is for, you can use it with &lt;code&gt;npx decap-server&lt;/code&gt; as indicated in &lt;a href="https://decapcms.org/docs/working-with-a-local-git-repository/" rel="noopener noreferrer"&gt;Working with a Local Git Repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A more encapsulated alternative
&lt;/h2&gt;

&lt;p&gt;Another alternative, which is the one used in an existing but a bit outdated &lt;a href="https://github.com/gxanshu/astro-decap-cms-starter" rel="noopener noreferrer"&gt;Astro Decap CMS Starter&lt;/a&gt; from &lt;a href="https://docs.astro.build/en/guides/cms/decap-cms/" rel="noopener noreferrer"&gt;offical Astro docs&lt;/a&gt; (which doesn't mean that the starter is official, it is actually archived because lack of time from the maintainer) is to use the CDN version of the library and even use a static html inside of the &lt;code&gt;/public&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;From a technical perspective it is not a bad alternative, since Decap will bootstrap itself there is not much to be processed by Astro at build time and the management of the information can be considered completely autonomous from the development.&lt;/p&gt;

&lt;h2&gt;
  
  
  A more customized version
&lt;/h2&gt;

&lt;p&gt;If you have read so far you will see, that I've already made a few choices, it is a matter of personal preference, but here I present what I consider the best option and why I prefer it over the alternatives.&lt;/p&gt;

&lt;p&gt;These are the important parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Code configuration instead of &lt;code&gt;config.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No pollution at &lt;code&gt;/public&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Allows for further customization.&lt;/li&gt;
&lt;li&gt;Privacy first, git-based but no need to expose access.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I prefer to have everything in a single place, so I created an admin route in &lt;code&gt;/src/pages/admin&lt;/code&gt; there is a minimal HTML file that doesn't use the regular layout, doesn't have anything inside the body but a script tag with the code to load and configure Decap, uploads are made into the &lt;code&gt;/src/assets&lt;/code&gt; folder so those can later be processed by Astro, and it feels like an automation of my regular flow instead of a different system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---

---

&amp;lt;html lang="en"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;link rel="icon" type="image/svg+xml" href="/favicon.svg" /&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width" /&amp;gt;
    &amp;lt;meta name="generator" content={Astro.generator} /&amp;gt;
    &amp;lt;title&amp;gt;Astro&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;script&amp;gt;
      import CMS from "decap-cms-app";

      const config = {
        localBackend: true,
        backend: {
          name: "proxy",
          proxy_url: "http://localhost:8081/api/v1",
        },
        load_config_file: false,
        media_folder: "/src/assets/images/",
        collections: [
          {
            name: "posts",
            label: "Posts",
            folder: "src/blog",
            create: true,
            fields: [
              {
                label: "Title",
                name: "title",
                widget: "string",
              },
              {
                label: "Body",
                name: "body",
                widget: "markdown",
              },
              {
                label: "Created",
                name: "created",
                widget: "datetime",
              },
              {
                label: "Tags",
                name: "tags",
                widget: "list",
              },
            ],
          },
        ],
      };

      CMS.init({ config });
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The manual initialization is described in &lt;a href="https://decapcms.org/docs/manual-initialization/" rel="noopener noreferrer"&gt;Customizing Decap CMS -&amp;gt; Manual Initialization&lt;/a&gt;. Apparently the line &lt;code&gt;window.CMS_MANUAL_INIT = true&lt;/code&gt; is not needed anymore.&lt;/p&gt;

&lt;p&gt;The main thing that I intentionally left unsolved for now is authentication, I don't need it and I think that configuring something that I don't need would increase security vulnerabilities and offer little gains.&lt;/p&gt;

</description>
      <category>astro</category>
      <category>decap</category>
      <category>cms</category>
      <category>git</category>
    </item>
    <item>
      <title>Building a Language Companion AI Agent</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Fri, 12 Dec 2025 05:42:37 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/building-a-language-companion-ai-agent-2llo</link>
      <guid>https://dev.to/migsarnavarro/building-a-language-companion-ai-agent-2llo</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-kaggle-ai-agents-2025-11-10"&gt;Google AI Agents Writing Challenge&lt;/a&gt;: Learning Reflections&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the last few weeks, I have spent considerable time learning about Google's ADK (Agent Development Kit) and putting together a capstone project for the AI agents course. I decided it was the right time to deep dive into AI and really commit to this project. It was a great time, not only because I found the time to work on some ideas I have had for a long time, but also because I feel I finally was able to catch up with this technology that has been evolving at a ludicrous speed.&lt;/p&gt;

&lt;p&gt;Coming from a front-end background (I do a lot of back-end these days, but that's another story), my first thought was to take the time to explore AI agents' user interface and user experience (UI &amp;amp; UX). In particular, I wanted to experiment with finding a way to use different, more specialized interfaces to accomplish specific tasks, without the feeling of having to switch context. &lt;/p&gt;

&lt;p&gt;I didn't have a clear idea of what exactly I was going to build, but I am really proud of what I accomplished, an agent that fits into another topic I am currently very passionate about, which is language education.&lt;/p&gt;

&lt;h2&gt;
  
  
  A language companion
&lt;/h2&gt;

&lt;p&gt;Before this capstone project, I had frequently used AI, but it was totally from the user perspective, with just a few brief incursions into development. I was not happy with the results in either area; it was the usual mediocre but tolerable result, enough to keep going, but not enough to catch your attention. &lt;/p&gt;

&lt;p&gt;By now, I had a clear idea about the project I wanted to work on, a problem I've been trying to solve with AI for a few months now: language learning. I started learning Mandarin a few years ago, and I've found AI to be a great companion. One of the drawbacks is that I lose a huge amount of time moving information around, from the conversation to the editor with constant jumps to the browser to look up videos or search for more information. I thought it would be great to have everything in the same place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5vey4lopukixoijh9qt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5vey4lopukixoijh9qt.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had a hard time getting started. Kaggle didn't help me this time, as a software engineer, it didn't feel right to code in a notebook. I have been using Kaggle for learning, and it is great, but this time was different because I needed to code in it. After a while, it started slowing me down and making it hard to think about the code and all the moving parts. &lt;/p&gt;

&lt;p&gt;With Kaggle's notebooks, things usually work out of the box, which is very cool, but they don't scale the way a properly structured project does. The Python in the learning material didn't look so much like the Python in the ADK examples; it seems it is just a matter of presentation, but there are some important runtime changes. Also, there have been quite a few changes in Python, and not being a full-time Python developer, I was not sure what tools to use. I learnt about uvicorn and typed Python for this project. The examples didn't just work on my machine. Ironically, I started by creating a Dockerfile for the project in order to have a clear view of the dev environment and no pollution from outside, which also meant keeping my computer clean.&lt;/p&gt;

&lt;p&gt;After spending the first few days struggling with the setup, trying to get it perfect, and not even touching the core of my project, I realized I had to change strategy if I wanted to finish my project in time. So I moved from working on getting the development environment right to getting the prompts right. My first attempt was to use a single agent to get some kind of MVP working, but the quality was not good enough. The reasoning was just fine, but then the agent had some sort of internal conflict with the separation of concerns. So I decided to create sub-agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One for inspecting the user's prompt goal&lt;/li&gt;
&lt;li&gt;One for getting the meaning&lt;/li&gt;
&lt;li&gt;One for getting the vocabulary&lt;/li&gt;
&lt;li&gt;Another one for finding common language patterns&lt;/li&gt;
&lt;li&gt;And, finally, one to summarize the work of the previous agents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI agents are inherently curious, and a small forgotten comment in a function description or some comment in a definition made the agent perform completely differently. For example, at some point, I had something like "English language" not even in the description of the agent but in the docstring for the agent file, so the agent systematically refused to accept sentences that were not in English, because it was intended for the English language, or so it said.&lt;/p&gt;

&lt;p&gt;It required a lot of effort to get this last sub-agent right. At the beginning, it didn't properly use the information of the other agents. I solved that problem, and then the root agent was trying to make the summary itself without using the summary the agent had just generated. Other times, it didn't call the sub-agent at all because it considered itself able to summarize the text. At some point, I wrote a conditional sentence that made the agent summarize the summary and totally ignored all the work done by the previous sub-agents.&lt;/p&gt;

&lt;p&gt;There are two main flows for my agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Asking to explain a phrase.&lt;/li&gt;
&lt;li&gt;Asking to explain a topic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those may sound similar, but think about the following. When you start with a topic, it is easy to come up with example phrases, but when you start with a phrase, it is not so easy to get back to the initial topic that originated it; there are so many topics that could have been the source of a given sentence.&lt;/p&gt;

&lt;p&gt;Now I had the information I wanted, but I still hadn't touched anything related to the interface yet. For a moment, I thought it would be great to have session, state, and long-term memory, and in the course notebooks, it seemed so easy, so I had to give it a try. It was hard to figure out how to configure things like the runner or the session when using the ADK, and I didn't want to sidetrack to that topic, so I had to skip memory and context, at least for the prototype.&lt;/p&gt;

&lt;p&gt;I spend a good amount of time exploring the possibilities of a human-in-the-loop pattern and the examples. That was the only mention of such a thing in the course, but it was not at all like the interface change I had in mind. I was thinking if it could be possible to create an interface with this concept, which is formally named elicitation, so I thought the safest way was to use an MCP server to deal with external calls in a coordinated and grouped manner. So I started creating an MCP server.&lt;/p&gt;

&lt;p&gt;FastMCP made the task of creating the MCP server incredibly easy. The only pain point was not being able to deploy it to their official cloud service. For now, I am running it locally with the streaming HTTP interface.&lt;/p&gt;

&lt;p&gt;It worked like a charm! The ADK picked up the context perfectly and generated the MCP request from the conversation. A page for the quiz was created, and the link was provided so the user could jump into the quiz. Once the user finishes the quiz, they can ask the agent to evaluate it. The answers are saved in an external database for persistence and to allow sharing them. Afterwards, the quiz is mostly graded by the AI agent, which means it is possible to assess open questions, something almost impossible without LLMs. &lt;/p&gt;

&lt;p&gt;I am really proud of the result. There is so much more work to do, but I loved the way ADK and FastMCP helped me create most of the boilerplate. I enjoyed coding the agent, and I will continue working on it to make sure it is production-grade sometime soon.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/-rMBhcqEyYU"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;There you have it: an AI-powered language tutor that includes creating quizzes that live outside of the main conversation and are really interactive. The main idea is to have a two-way communication from the conversational AI agent to the quizzes platform and back, the last part will remain as an exercise to the reader... ehem, sorry, as a goal for another time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The course
&lt;/h2&gt;

&lt;p&gt;The course was structured in a really intuitive way, but I still struggled with one particular aspect: The workflow for using the ADK to build the agents. Kaggle notebooks are an incredible research setup, but they are very different from the environment in which the agent will run in production. Here are some of the questions I found along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to structure an ADK project?&lt;/li&gt;
&lt;li&gt;How to run ADK in Python, or go?&lt;/li&gt;
&lt;li&gt;What does the app runner mean? Can I have it in my main project file?&lt;/li&gt;
&lt;li&gt;How to configure things in a repository (ex. session, state, and memory)?&lt;/li&gt;
&lt;li&gt;How to efficiently test things? Not to evaluate the agent performance, but to work in a section of a multi-agent architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the beginning, I was trying to build my project as I went along with the course, but it turned out to be impractical, too much information, too little time, and so many unexpected problems. The way I understand agents changed a lot through the course. Initially, I had no clear idea of the building blocks and the possibilities, but I also didn't know how much I knew because of my particular skills and experience. I thought it would be easier, not in a magical sense, but I thought ADK would take care of more things, not that I think it is not necessary, on the contrary, it helps a lot, and taking care of more stuff would mean a trade-off in terms of flexibility.&lt;/p&gt;

&lt;p&gt;After the course and the capstone project, my priority list would be as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding agents (flows, agent vs tool, inter-agent communication)&lt;/li&gt;
&lt;li&gt;How to code so as not to block yourself in the future? If you have a good perspective, you can start small and grow incrementally without having to restructure the whole project.

&lt;ul&gt;
&lt;li&gt;How to think: bottom-up, top-down? How to isolate agents for development?&lt;/li&gt;
&lt;li&gt;MCP, A2A.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Interfaces. In my opinion, it is the most important understated aspect.&lt;/li&gt;

&lt;li&gt;Memory and Session. It is foundational, but you cannot get it right if you don't solve the other problems first.&lt;/li&gt;

&lt;li&gt;Testing. How to keep a prompt library and implement testing so you don't waste time. Evaluation to have a way to compare previous and current performance.&lt;/li&gt;

&lt;li&gt;Deployment. It depends a bit on the structure of the project and the intended interface, but it is frustrating to spend much time building something and discover it is not suited to be deployed as intended.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  AI is much more than LLMs
&lt;/h3&gt;

&lt;p&gt;I was completely unaware that such a vibrant and diverse community existed around artificial intelligence. I had started creating some AI-powered software before, and then abandoned the project after a while, feeling a bit lost and disappointed.&lt;/p&gt;

&lt;p&gt;This AI agents course was great because it opened a new perspective on how all the pieces of this ecosystem fit together. I think it is worth mentioning that there are many different startups doing many different but complementary things. From the outside, only the big names are heard, but for every detail of the workflow, at least a few different companies are building great products. As my idea evolved, while I was searching for tutorials and ways to make things fit together, I started learning about some incredibly specialized details of the AI ecosystem.&lt;/p&gt;

&lt;p&gt;AI is amazing; you really have the knowledge of an expert in any field at your fingertips, but at the same time, you have the naivete of an 8-year-old kid doing her best to prove that she is old enough to do a real job. So, with AI, you need to be more patient and iterate a lot, way more than with usual engineering, even if you think you really have to iterate a lot for usual engineering work.&lt;/p&gt;

&lt;p&gt;I think there is a real shift, and something I've not seen mentioned that often is that AI was built by engineers but designed by managers; it is not for the excellent engineer who needs to be more productive, it is, mostly, for managers who can achieve a higher throughput through coordination of several human or digital agents. That's not to start a discussion on whether it is good or bad, just to say that we really need to understand what AI can do for us in practical terms, and that often comes as an improvement through management.&lt;/p&gt;

&lt;p&gt;I am convinced it is an exciting time in an emerging area, and there are so many new things to come.&lt;/p&gt;

&lt;h2&gt;
  
  
  Afterthoughts
&lt;/h2&gt;

&lt;p&gt;At the end, I realized creating agents doesn't simplify building tools at all; it just makes it a different kind of job, for a totally different profile. It may be more enjoyable, because it transforms the tasks from a form we struggle to deal with into a form we are more comfortable using. It makes complex tasks simpler; we just have to ask for them.&lt;/p&gt;

&lt;p&gt;It is mentioned that anyone can build with AI. It is true to some extent, since the interface is simplified compared to having to learn artificial languages to express your simpler thoughts, at the same time is also false, because most people have no management skills at all, and the marketing makes them believe it is just talking, while it is not; complex projects remain complex even if expressed as a conversation. &lt;/p&gt;

&lt;p&gt;Creating an agent is about thinking like the leader of a team and coordinating the rest of the team to do their job most purposefully and efficiently. If you don't know what you want to achieve, it is easier than ever to get lost because now not only can other humans take the leadership away from you, but machines can too. It is way too easy to sidetrack.&lt;/p&gt;

&lt;p&gt;Here are my two main takeaways from the course:&lt;/p&gt;

&lt;p&gt;First, management is more important than ever, not less. We often focus on the technical part of state-of-the-art technologies, and end up neglecting the essential part that is managing, not just managing people, but managing time, managing resources. AI is often portrayed as a magical technology that can help us do everything we want, but it is not; it is as good as the prompts we can create for it and the uses we decide to give it. We can create a prompt creator and a task manager agent, but to create a good prompt creator or task manager, we need to go back to step one. &lt;/p&gt;

&lt;p&gt;Second, for complex problems, AI agents won't save you time. But don't despair; it can help a lot by transforming the job from one type of job in which humans are not very capable to another one in which we are really good at, which is expressing and sharing our thoughts. Building a good agent takes a lot of time and a systematic engineering approach; the resulting agent will only be as good as the team that constructs it. It is a good idea to use AI to build AI, but it won't save much time; it will just make the task more manageable.&lt;/p&gt;

&lt;p&gt;A final remark I would like to make. While it is true that human beings' skills are limited, we do have a word or two about the way we shape our society. Thoughts are continuously being re-shaped, and we should care that new generations discover the joy of science, engineering, and many other topics that are currently labeled as difficult or boring. AI agents are incredible indeed, and it's precisely because of that that it is paramount to know how to use and coordinate them with mastery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.kaggle.com/competitions/agents-intensive-capstone-project/writeups/language-learning-companion-agent" rel="noopener noreferrer"&gt;Kaggle Writeup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sarmignav/language-companion" rel="noopener noreferrer"&gt;Agent repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sarmignav/mcp-quiz" rel="noopener noreferrer"&gt;MCP repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleaichallenge</category>
      <category>ai</category>
      <category>agents</category>
      <category>devchallenge</category>
    </item>
    <item>
      <title>First steps with Caddy</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Fri, 07 Nov 2025 15:38:08 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/first-steps-with-caddy-3bmc</link>
      <guid>https://dev.to/migsarnavarro/first-steps-with-caddy-3bmc</guid>
      <description>&lt;p&gt;As part of my effort to securely host my own &lt;a href="https://www.filestash.app/" rel="noopener noreferrer"&gt;Filestash&lt;/a&gt; instance I've been playing around with &lt;a href="https://caddyserver.com/" rel="noopener noreferrer"&gt;Caddy&lt;/a&gt; in an effort to better understand how to use it to serve as a very basic gateway for my server.&lt;/p&gt;

&lt;p&gt;Caddy configuration can be quite complex, from what I've seen in other places. Happily, it can also be quite simple and straightforward for the more basic scenarios. In any case, it is good to learn some of the concepts behind caddy's configuration, those will be very helpful for any kind of use case, but I feel the docs can be overwhelming sometimes.&lt;/p&gt;

&lt;p&gt;Caddy &lt;strong&gt;has three ways of configuring it&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Caddyfile&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;caddy.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more expressive format and the one used under the hood is the JSON configuration, but it can be harder to read than the &lt;code&gt;Caddyfile&lt;/code&gt;. The API and the JSON file offer more flexibility for automated configuration. In this post I will only discuss the &lt;code&gt;Cadyfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It &lt;strong&gt;is possible to automatically generate the JSON file from the &lt;code&gt;Caddyfile&lt;/code&gt;&lt;/strong&gt; using a the &lt;code&gt;adapt&lt;/code&gt; command that is provided as part of the CLI:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;caddy adapt -pc ./Caddyfile&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here, the &lt;code&gt;-p&lt;/code&gt; option means pretty, to get the JSON format with newlines and tabs, although I don't really like it since I am used to 2-space indentation, the &lt;code&gt;-c&lt;/code&gt; option is to explicitly refer to the file used, if omitted it looks for a &lt;code&gt;Caddyfile&lt;/code&gt; in the current directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building blocks of a Caddyfile
&lt;/h2&gt;

&lt;p&gt;The Caddyfile is composed of &lt;strong&gt;blocks&lt;/strong&gt; and &lt;strong&gt;directives&lt;/strong&gt;, you can see a more detailed introduction in &lt;a href="https://caddyserver.com/docs/caddyfile/concepts" rel="noopener noreferrer"&gt;caddyfile :: concepts&lt;/a&gt; section of the docs. &lt;strong&gt;Blocks&lt;/strong&gt; group directives, and &lt;strong&gt;directives&lt;/strong&gt; are functional keywords, that is, can be translated into actions to take, a list of directives is available &lt;a href="https://caddyserver.com/docs/caddyfile/directives" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The last fundamental concept to understand is the &lt;strong&gt;matchers&lt;/strong&gt;, as the name suggests the matchers match something, in Caddy thay can match several different things, like paths, methods, query variables, etc.&lt;/p&gt;

&lt;p&gt;With these three things we can do a lot. We will often see three directives in the examples, &lt;strong&gt;respond&lt;/strong&gt; that sends something as a response to the client, &lt;strong&gt;reverse_proxy&lt;/strong&gt; that forwards the requests to another server, and &lt;strong&gt;file_server&lt;/strong&gt; that is for static files server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Matchers
&lt;/h2&gt;

&lt;p&gt;Now I feel it very intuitive, but I still remember a few days ago I was a bit confused with the concept of a matcher, since I was expecting something more rigid. I was thinking about matching strings, paths in particular, and then considering the rest of the stuff, like headers, cookies, or other request properties, and I think all of this was because that would be the way I would have structured the problem in javascript, or other similar routers for servers in which you have middleware to which a &lt;code&gt;request&lt;/code&gt; and a &lt;code&gt;response&lt;/code&gt; objects are passed.&lt;/p&gt;

&lt;p&gt;Caddy's matchers are way more flexible by placing many of the request properties at the same level as the path, so you can create a matcher for ip, headers, host, path, method, protocol, or query among other things. This allows for an incredible flexibility. You can, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use paths to decide which server handles it.&lt;/li&gt;
&lt;li&gt;Serve a difference response based on ips.&lt;/li&gt;
&lt;li&gt;Have different services based on headers or query variables.&lt;/li&gt;
&lt;li&gt;Use a host matcher for subdomains&lt;/li&gt;
&lt;li&gt;Handle websockets using protocol and headers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It took me some time to wrap my head around this concept, but once I understood how it worked I was amazed by the simplicity and power it provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Super basic auth with a matcher
&lt;/h2&gt;

&lt;p&gt;This &lt;strong&gt;is NOT something you should use for production&lt;/strong&gt;, but I was looking for a quick way to protect my server, and I found that using a header was so straightforward that is not even documented.&lt;/p&gt;

&lt;p&gt;Simple HTTP auth is also included by default and it may be more secure, although it is very similar in my opinion, you can also use cookies or ip following the same approach. For me, what was important was not having to create some HTML form and have some server code to process it, but I need to say it one more time, I wanted a quick solution for me as a single user, with my Proof-of-Concept, it is not something I would use for something more serious.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
localhost:3000 {
    @protected {
        path /protected/*
        header token here-is-my-token
    }

    respond @protected "I'm protected"
    respond "I'm public"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here is that we use a named matcher, the block defined with &lt;code&gt;@protected&lt;/code&gt; in one of the respond directives, this works like an &lt;code&gt;if&lt;/code&gt; that will only execute when the conditions of the matcher are met.&lt;/p&gt;

</description>
      <category>caddy</category>
      <category>caddyfile</category>
      <category>server</category>
      <category>auth</category>
    </item>
    <item>
      <title>Caddy, self-signed certificates and Certificate Authorities for web development</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Thu, 06 Nov 2025 13:53:04 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/caddy-self-signed-certificates-and-certificate-authorities-for-web-development-653</link>
      <guid>https://dev.to/migsarnavarro/caddy-self-signed-certificates-and-certificate-authorities-for-web-development-653</guid>
      <description>&lt;p&gt;This is a very quick post, just to summarize some of the last days findings. Often the answer to the questions is explicit once you know about it, but it is very hard to find out when you don't know what you are looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  0. Using Caddy HTTPS for localhost
&lt;/h2&gt;

&lt;p&gt;I have known for a long time that Caddy is good because it offers out-of-box Let's Encrypt (and ZeroSSL) certificates, but I didn't know how to use them, because the last time I tried to use them for development it was a pain. I decided to give it another try. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;first thing to know&lt;/strong&gt; is that you need to explicitly set localhost for the site in order to Caddy try to use a self signed certificate. It can later try to configure it, but it seems to need to install a library for that, also, I was using Podman as a why to keep my machine as clean as possible, so I didn't want to install the library, I think it is just for the automatic configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Where Caddy stores self signed certificates?
&lt;/h2&gt;

&lt;p&gt;I am using containers, and the &lt;a href="https://hub.docker.com/_/caddy" rel="noopener noreferrer"&gt;official Caddy image&lt;/a&gt; has some really nice and useful documentation, the problem is I didn't pay attention because I was not aware I was going to use it.&lt;/p&gt;

&lt;p&gt;No matter how you installed caddy &lt;code&gt;caddy environ | grep caddy&lt;/code&gt; will tell you some information about the data and config directories, look for &lt;code&gt;AppDataDir&lt;/code&gt; and &lt;code&gt;AppConfigDir&lt;/code&gt;, respectively. For my container I got the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;caddy.HomeDir&lt;span class="o"&gt;=&lt;/span&gt;/root
caddy.AppDataDir&lt;span class="o"&gt;=&lt;/span&gt;/data/caddy
caddy.AppConfigDir&lt;span class="o"&gt;=&lt;/span&gt;/config/caddy
caddy.ConfigAutosavePath&lt;span class="o"&gt;=&lt;/span&gt;/config/caddy/autosave.json
caddy.Version&lt;span class="o"&gt;=&lt;/span&gt;v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are using containers, the documentation says &lt;strong&gt;it is important to mount persistent volumes for &lt;code&gt;/data&lt;/code&gt; and &lt;code&gt;/config&lt;/code&gt; directories&lt;/strong&gt;, and it is, even if you don't care about the cpu and memory usage, if you create everything each time you will have to configure things again.&lt;/p&gt;

&lt;p&gt;The second thing is that by using a named volume for that information, it is easy to use the files from the host without "polluting" your system, at least it will be more explicit if you eventually remove those files.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Configuring curl and your browser.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Which file I need to use?
&lt;/h3&gt;

&lt;p&gt;First, it is important to know which file to use, it is &lt;code&gt;/data/caddy/pki/authorities/local/root.crt&lt;/code&gt; where pki means Public Key Infrastructure, and we need the public part of the root certificate, I tried to look for the reference where I found it, but I was out of luck.&lt;/p&gt;

&lt;h3&gt;
  
  
  Firefox
&lt;/h3&gt;

&lt;p&gt;I think it is similar for any browser but for simplicity I'll cover only firefox. You need to open &lt;code&gt;about:preferences#privacy&lt;/code&gt;, you need to scroll down until you see &lt;code&gt;Security&lt;/code&gt; and &lt;code&gt;Certificates&lt;/code&gt;, then click the &lt;code&gt;View Certificates&lt;/code&gt; button to open the &lt;code&gt;Certificates Manager&lt;/code&gt;, be sure to be in the &lt;code&gt;Authorities&lt;/code&gt; tab, then click &lt;code&gt;Import...&lt;/code&gt; and select the file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiah2j01dor906hhhdgh7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiah2j01dor906hhhdgh7.png" alt=" " width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new certificate authority from &lt;code&gt;Caddy Local Authority&lt;/code&gt; will appear and now you can use HTTPS locally without warnings and additional validation steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Curl
&lt;/h3&gt;

&lt;p&gt;For curl we have two ways of working with HTTPS, the proper way, and the workaround.&lt;/p&gt;

&lt;p&gt;The workaround is using the option &lt;code&gt;-k --insecure&lt;/code&gt;, quick and useful, but it can grow into a bad habit. The proper way is to use the &lt;code&gt;--cacert&lt;/code&gt; and point to the same root certificate:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl --cacert ./data/caddy/pki/authorities/local/root.crt https://localhost:3000/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The only thing to be careful about is the path of the certificate, it  would be nice to be able to configure that, in a per project basis, but I'll leave that for later.&lt;/p&gt;

</description>
      <category>security</category>
      <category>caddy</category>
      <category>tls</category>
      <category>https</category>
    </item>
    <item>
      <title>Filestash Plugin Tutorial: Auto‑Login to S3 Without Typing Credentials</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Sun, 26 Oct 2025 03:54:08 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/writing-a-filestash-plugin-3350</link>
      <guid>https://dev.to/migsarnavarro/writing-a-filestash-plugin-3350</guid>
      <description>&lt;p&gt;&lt;del&gt;This morning&lt;/del&gt; Three days ago I finally got my Filestash plugin working as intended, after a week spending my early mornings working on that. This is just the first step, I am aware of that, and the non-coding part often takes longer than the coding part. The purpose of my plugin is really simple, I wanted to be able to set a back-end account in the configuration, so I don't have to login my s3 credentials every time I want to use the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit 2025/10/31:&lt;/strong&gt; Here is the &lt;a href="https://github.com/migsar/filestash/tree/migsar_exploring-filestash/server/plugin/plg_plugin_automatic_login" rel="noopener noreferrer"&gt;link to my repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I discovered Filestash I really loved its simplicity, sadly, as it often happens, I am discovering it is not so simple under the hood, not a bad thing, just to state some often not told truth, product simplicity very often do not correlate to technical simplicity.&lt;/p&gt;

&lt;p&gt;The main reason to write this post is to help others in that regard, contribute to make Filestash a bit more simpler technically, or at least easier to understand.&lt;/p&gt;

&lt;p&gt;In my previous post, &lt;a href="https://dev.to/migsarnavarro/a-very-basic-plugin-for-filestash-1elf"&gt;A very basic plugin for Filestash&lt;/a&gt;, I described some of the problems I faced when I got started working with Filestash source code, this post builds on top of that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do I need a plugin?
&lt;/h2&gt;

&lt;p&gt;I've been paying for a s3 compatible storage for a few months, and I really need it, but often I skip using it because of the lack of a UI, there are a few UIs already, but none of them adjust to my needs, so when I came to an open source project like Filestash, I thought it was perfect.&lt;/p&gt;

&lt;p&gt;Since I don't think my use case is standard, I'll give you a few more details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I want to use Filestash as a GUI to manage my s3 compatible object storage.&lt;/li&gt;
&lt;li&gt;I want to upload files, particularly photos and audio from a few different sources, particularly my cellphone (I guess it is not that common to use s3 object storage, and probably it is more common to pay for a backup service or for something like Google Photos but hold on, I'll explain).&lt;/li&gt;
&lt;li&gt;I want to use the files in my s3 storage seamlessly in web-applications, Jupyter notebooks, and all other sort of network based experiments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In contrast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don't need just a web file manager to move files between computers and the cloud.&lt;/li&gt;
&lt;li&gt;I won't be sharing the access to this storage, I'll be the only one using it.&lt;/li&gt;
&lt;li&gt;I will be sharing, some of the files I have in the storage, without ever showing Filestash UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've have not found a solution for what I need, and if you know about some nice solution please share it, I would love to hear about it. I need a really simple product totally aligned with my needs because any friction will make me not use it. &lt;/p&gt;

&lt;p&gt;One last non-technical but very important reason. I'll do the development myself because I enjoy coding when it has a purpose, and I don't have the budget to hire someone else to do it, nor the patience to manage it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem definition
&lt;/h2&gt;

&lt;p&gt;My problem was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I need to initialize an s3 backend (although it should be similar for other backends) so I can open the app and I don't have to type the credentials for the backend any time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pr234c3ptjuyww55wsi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pr234c3ptjuyww55wsi.png" alt="Filestash login screen" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;
I want to avoid this screen by automatically login the user



&lt;p&gt;From previous experiences I knew it was a good idea to take a look at the Github's repository issues, to see if other people have experienced or probably solved the same problem. I didn't find something similar to what I need, there was one open issue but it was really new and didn't have any useful information.&lt;/p&gt;

&lt;p&gt;The first step was a quick product inspection, I started by opening the dev tools in the browser and taking a look at what happened when I run the app and do all the stuff manually. It was not as linear as I describe it next but it was not hard at all. As an example of the lack of linearity, just two days ago I suddenly realized I had not explored how the demo app authentication happened.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The demo app actually uses a request to &lt;code&gt;/api/session&lt;/code&gt; with the credentials from the home page of filestash so the cookie is created. It is a quick solution that could work but I don't really like it because of two things: the first and most important is that it exposes the credentials, something that I didn't wanted to do, the second is that is not elegant at all (of course this is just my opinion).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;My initial approach was to skip authorization altogether, just initialize the back-end and directly jump to the files view. It may be possible, but I realized that for that approach to work I would need to edit the core, something really want to avoid. So I decided to find a way to make it work as a plugin, even if I already know compilation was needed to configure the plugin.&lt;/p&gt;

&lt;p&gt;I didn't have any idea of what the app flow was, I had an idea of how I would architecture a similar application, but that didn't mean there were not other ways to achieve the same things.&lt;/p&gt;

&lt;p&gt;From doing things manually I noticed that the first time I was immediately redirected to &lt;code&gt;/login&lt;/code&gt;, but there was no login in the routes, so I assumed it was something in the browser. Then I realized that &lt;code&gt;/&lt;/code&gt; was taken care at the end of all the routes in a catch all block. When I filled the form and click the submit button a POST request was made to &lt;code&gt;/api/session&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvxlnw7zgk21hl2kd8ih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvxlnw7zgk21hl2kd8ih.png" alt="Current login flow" width="394" height="458"&gt;&lt;/a&gt;&lt;/p&gt;
The user needs to send the storage credentials to the server.



&lt;p&gt;It took me some time to discover the logic for the front-end, partly because it is a custom vanilla-js but react-like solution. At some point I read the discussion about keeping a React app or moving to vanilla-js, I don't like the new solution but hey, with open source is like this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Routing
&lt;/h3&gt;

&lt;p&gt;The router is located in &lt;code&gt;server/routes.go&lt;/code&gt;. There are three routes we care about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /&lt;/code&gt;, it uses &lt;code&gt;ServeFrontofficeHandler&lt;/code&gt; handler. This is the entry point, I don't want to type a complex path before viewing the content.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/session&lt;/code&gt;, it uses &lt;code&gt;SessionAuthenticate&lt;/code&gt; handler and creates a cookie. This route is used when the user submits the credentials in the &lt;code&gt;/login&lt;/code&gt; page, which apparently is just a browser view in a SPA.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/session&lt;/code&gt;, it uses &lt;code&gt;SessionGet&lt;/code&gt; handler. This is the endpoint the home page hits to know if the user is logged in, or more accurately, if the storage has been initialized, and then to decide what view to show accordingly. If the user is logged in the view will be the filesystem, otherwise it will be the login page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An important thing to notice is that each one of them uses a different set of middlewares. Most of them are not really important, but as I was trying to understand how things worked I found &lt;code&gt;SessionStart&lt;/code&gt; and &lt;code&gt;BodyParser&lt;/code&gt; were relevant.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SessionStart&lt;/code&gt; injects the session for the request context, so even if a backend has been initialized, some session variables won't be available unless the route uses this middleware.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BodyParser&lt;/code&gt; injects the body of the request into the context. Not sure why this is necessary, but it took me a while to figure that the  absence of &lt;code&gt;ctx.Body&lt;/code&gt; was breaking my code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hooks: The best place to inject my code
&lt;/h3&gt;

&lt;p&gt;It was not obvious to me where to put my code. The hooks system is not documented, I already knew the possible hooks, but I didn't know what were the entry points for those, also, at the beginning I didn't  know how the server persisted the state.&lt;/p&gt;

&lt;p&gt;My initial thought was to use the &lt;code&gt;Onload&lt;/code&gt; hook, since I wanted to start the backend before any request and keep it initialized, but then I found that this hook does not inject the &lt;code&gt;App&lt;/code&gt; to their handlers, so I was not able to save the initialization once done.&lt;/p&gt;

&lt;p&gt;Then I thought about using &lt;code&gt;Middleware&lt;/code&gt; or &lt;code&gt;HttpEndpoint&lt;/code&gt;, to be honest I still don't know if I choose the right thing, even if my code will impact requests I don't really feel like it is a middleware, it should be run just once at the beginning, but there is an advantage of running it as middleware, and it is that I am able to send the cookie to the front-end once I finished initializing the backend.&lt;/p&gt;

&lt;p&gt;So I ended up registering it as middleware but running it only once for the given path, this offers some flexibility but I am not totally happy with this solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  The journey
&lt;/h2&gt;

&lt;p&gt;My plugin doesn't need to do anything new, and I was aware of that, it was more about discovering where things were being done, and then see how much I was able to re-use, the main purpose was to slightly modify the app flow.&lt;/p&gt;

&lt;p&gt;My biggest problem was to define what the flow should look like, with the usual flow you have a session because you have some state that is session dependent, in this case, the storage back-end is user defined, but if I want to have a fixed storage back-end I don't see the need for a session, maybe it is important in the server to keep a connection open for some time for the sake of efficiency, but from the user perspective, you don't need a session anymore. The implicit decision in this case is that the logout button that appears on the top right of the UI is not needed anymore.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshuaqjmulhrq2t36sove.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshuaqjmulhrq2t36sove.png" alt="Automatic login flow" width="522" height="388"&gt;&lt;/a&gt;&lt;/p&gt;
The login happens in the server without user input



&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;This is a very important topic and one that usually is difficult to understand and to implement properly. I am not an expert in authentication but after ten years working with very different code bases I've seen many different ways to achieve the same things. Often in sub-optimal ways, some times I've helped improve the code, some other times I've not been able to convince the team the current solution is not optimal or even faulty, sometimes, I've only been able to detect the flaws retrospectively, after seeing it properly solved in a different company or after working it for myself in one of my projects.&lt;/p&gt;

&lt;p&gt;Filestash offers some authorization middleware, and some authentication middleware, but I didn't manage to configure it properly. Even after disabling absolutely everything, there is the need for a session, both for the sake of efficiency and security. In the code it can be read something like &lt;code&gt;isAuthorized&lt;/code&gt; but it refers to the authorization from the point of view of the storage service provider, which could or could not be the same authorization that the app will use. As an example, for my use case, I would like to hide the storage provider authorization from the user of Filestash, which will be just me, al least initially.&lt;/p&gt;

&lt;p&gt;This storage authorization is sent to the browser, probably for the lack of another method which would be implemented as part of the authorization or authentication middleware, but while I was exploring the code I didn't find flags to disable this to happen or to make it work in a different way but I am not 100 % sure if it cannot be done or simply I missed it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal state
&lt;/h3&gt;

&lt;p&gt;The app saves everything in the App structure which is defined in &lt;code&gt;server/common/app.go&lt;/code&gt; and often injected as the &lt;code&gt;ctx&lt;/code&gt; parameter. And important note here is that there is a &lt;code&gt;Context&lt;/code&gt; field in the struct but it doesn't seem to be used for the session. Here are the four fields that were relevant for the plugin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Backend&lt;/code&gt; saves a reference to the client used for accessing the storage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Body&lt;/code&gt; is not used directly, but it is used by &lt;code&gt;SessionAuthenticate&lt;/code&gt; by including &lt;code&gt;BodyParser&lt;/code&gt; as a temporary storage for the POST body.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Session&lt;/code&gt; stores the credentials for connecting to the storage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Authorization&lt;/code&gt; stores the same hash that is included in the cookie.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Front-end
&lt;/h3&gt;

&lt;p&gt;At the moment there was nothing to be done, but at some point in the future I would like to add a route and a page to set there the login information and some other session parameters, like deciding in which page or pages inject the cookies, or the home directory for the storage.&lt;/p&gt;

&lt;p&gt;The only part that we care about is &lt;code&gt;public/assets/pages/ctrl_homepage.js&lt;/code&gt;, this is executed for the home page, that is the &lt;code&gt;/&lt;/code&gt; path, and there a GET request to &lt;code&gt;/api/session&lt;/code&gt; is done to know if a session already exists.&lt;/p&gt;

&lt;p&gt;The plugin does not modify this, but send the cookie in the &lt;code&gt;/&lt;/code&gt; request so when the call to &lt;code&gt;/api/session&lt;/code&gt; is made the cookie is already there, without ever asking for the credentials, so the files are displayed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The cookie
&lt;/h3&gt;

&lt;p&gt;Cookies are using to keep track of both the user and admin sessions. There is not much to say in this regard, still I think it is important to create a different section to make it visible. The functions for encrypting and decrypting are included in &lt;code&gt;server/common/crypto.go&lt;/code&gt; and the secret used is defined in the settings section of the admin panel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is a basic plugin and there are still plenty of room for improvements, the most important thing is that currently this only works for s3 storage, which solves my problem but is very limited compared to the wide range of storage that Filestash support. Another nice thing to have would be a plugin configuration page, but for the moment it solves a problem. The next days I'll work in getting Filestash to work with a reverse proxy (Caddy) and deployed it to production.&lt;/p&gt;

</description>
      <category>filestash</category>
      <category>go</category>
      <category>plugins</category>
      <category>auth</category>
    </item>
    <item>
      <title>Debugging in Go with Delve</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Sun, 19 Oct 2025 18:56:11 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/debugging-in-go-with-delve-32a3</link>
      <guid>https://dev.to/migsarnavarro/debugging-in-go-with-delve-32a3</guid>
      <description>&lt;p&gt;I'm not a go developer, yet, although I am considering it more seriously these days. It's been a week since I started working in the Filestash project, I've not yet managed to get things working but in the process I've been learning a bit of go.&lt;/p&gt;

&lt;p&gt;At some point I really needed to know the internal state of the application at given times, so I decided to explore how to debug with go. I know that many developers stick to string output, but I've always been a fan of the debuggers, even for javascript, but I don't follow the state-of-the-art debugger news. So it didn't take much time to find about &lt;a href="https://github.com/go-delve/delve" rel="noopener noreferrer"&gt;Delve&lt;/a&gt;, a really friendly open source go debugger.&lt;/p&gt;

&lt;p&gt;I think the documentation could be better structured, but to be fair, I only used it to install it, and after that it was really intuitive and when I needed help I used the &lt;code&gt;help&lt;/code&gt; option from the CLI.&lt;/p&gt;

&lt;p&gt;Christopher Berge from Applied Go created a nice and more detailed &lt;a href="https://appliedgo.net/spotlight/delve-cheat-sheet/" rel="noopener noreferrer"&gt;cheatsheet&lt;/a&gt;, I will talk very briefly about how intuitive it was for a js developer to use it, and about the things that were not so intuitive.&lt;/p&gt;

&lt;p&gt;First, installation was straightforward, I connected to the container instance I am using and installed it directly, I still need to add it to the container file.&lt;/p&gt;

&lt;p&gt;As part of a lapsus I wrote &lt;code&gt;break&lt;/code&gt; in a source file, just to find a compiler error saying it was not possible to write a break outside a conditional structure or something like that. The easiest way to start Delve was to run it from the directory of the entry-point, in the case of Filestash, it is &lt;code&gt;/cmd&lt;/code&gt;, then I run &lt;code&gt;dlv debug&lt;/code&gt;, the first time it took some time, but after that it has been really fast.&lt;/p&gt;

&lt;p&gt;The breakpoints are added from the CLI without the need to edit the source files, a feature I had never used but its quite nice, you can use the relative path and the line number in the common syntax:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;break server/ctrl/session.go:32&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then you need to write &lt;code&gt;continue&lt;/code&gt; or &lt;code&gt;c&lt;/code&gt; to start the execution, if you want to stop execution you can press &lt;code&gt;ctrl+c&lt;/code&gt; at any point. You can quit the program with &lt;code&gt;exit | quit | q&lt;/code&gt; and restart execution with &lt;code&gt;restart | r&lt;/code&gt;. I think when you just press enter it repeats the last command you used, but I discover that empirically and it may be wrong or incomplete.&lt;/p&gt;

&lt;p&gt;Then there are a few handy commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pb&lt;/code&gt; prints breakpoints&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clear n&lt;/code&gt; delete breakpoint n&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clearall&lt;/code&gt; delete all breakpoints&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;p&lt;/code&gt; print a variable or evaluates an expression&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many more commands, but to be honest, since I am debugging a web app under normal circumstances, mostly to learn about it, I only needed those few comments. I was quite happy using it, and it helped me a lot to move faster than I would otherwise done.&lt;/p&gt;

&lt;p&gt;One thing I don't fully understand, but my understanding of go programs is very limited at the moment, is that it seems to use a separated cache/config from the binary program, which is nice, since it allows to experiment freely, but it is something I would love to dig a bit deeper in the days to come.&lt;/p&gt;

</description>
      <category>go</category>
      <category>debugger</category>
      <category>delve</category>
    </item>
    <item>
      <title>A very basic plugin for Filestash</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Wed, 15 Oct 2025 09:26:57 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/a-very-basic-plugin-for-filestash-1elf</link>
      <guid>https://dev.to/migsarnavarro/a-very-basic-plugin-for-filestash-1elf</guid>
      <description>&lt;p&gt;This post is about how to create a very simple plugin for Filestash, the plugin does nothing at all but writing a line in the logs, it is not intended to be a guide for plugin development, just to summarize the first steps I didn't found anywhere else so myself or other developers can save a few minutes next time.&lt;/p&gt;

&lt;p&gt;A few days ago I published &lt;a href="https://dev.to/migsarnavarro/a-docker-development-environment-for-filestash-4m7n"&gt;A Docker development environment for Filestash&lt;/a&gt; in which I showed how to create a quick and simple development environment for &lt;a href="https://www.filestash.app/" rel="noopener noreferrer"&gt;Filestash&lt;/a&gt;, a web file manager. As I explore the software, I've been discovering things I would love to document, I think the best way to document it is writing this kind of practical, although very simplistic posts.&lt;/p&gt;

&lt;p&gt;I am not a go programmer, so expect comments that may be obvious for developers familiarized with the language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do I need to create a plugin?
&lt;/h2&gt;

&lt;p&gt;Filestash's architecture is modular, and plugins play a huge role. Plugins are used for adding storage types, for authentication, authorization, for handling different types of files, for using different types of servers (starter plugins), etc. I am not 100% sure, but it seems most Filestash tasks are achievable through its plugin system.&lt;/p&gt;

&lt;p&gt;The sad part is that I didn't find much documentation about how to customize things. At the beginning I didn't even know how to enable/disable plugins. Eventually this made me create a plugin to try to figure out how the system works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The entry point: how to enable/disable plugins
&lt;/h2&gt;

&lt;p&gt;Plugins are added to the app in &lt;code&gt;server/plugin/index.go&lt;/code&gt;, there you have to add or remove the reference for the plugin package in the imports.&lt;/p&gt;

&lt;p&gt;There are things you need to know about imports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;_&lt;/code&gt; is called blank and means that the package is imported for its side effects, that is, the code in its initialization.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.&lt;/code&gt; is called a period, and it means that all the package's exported identifiers will be available in the importing package without a qualifier, which usually is the name of the imported package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One additional note about go is that &lt;em&gt;init&lt;/em&gt; is a special function that is automatically executed when the package is initialized. This happens before the main function runs and after all package-level variables are initialized.&lt;/p&gt;

&lt;p&gt;In conclusion, to add the plugin we simply need to add it to the imports in &lt;code&gt;server/plugin/index.go&lt;/code&gt;, to disable a plugin we can comment out or delete the importing line in this file.&lt;/p&gt;

&lt;h2&gt;
  
  
  A new plugin
&lt;/h2&gt;

&lt;p&gt;To have a package to import we need to create it, here is the simplest plugin I could think of that has a visible output.&lt;/p&gt;

&lt;p&gt;It does one simple thing, write an output line to the logs. Even so, there are some interesting remarks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The logger is defined in the &lt;code&gt;common&lt;/code&gt; package, so we need to import it. We could import &lt;code&gt;fmt&lt;/code&gt; instead and use print, to immediately print the text, but it would feel out of chronological order.&lt;/li&gt;
&lt;li&gt;If we plainly write the log it won't work because the logger is not ready, the  best way to do it is to put our function inside a lifecycle hook for the app, more on this in the next section.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;plg_plugin_example&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"github.com/mickael-kerjean/filestash/server/common"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Apparently Log is not initialized at this point&lt;/span&gt;
    &lt;span class="c"&gt;// Log.Debug("Why this is not printed?")&lt;/span&gt;
    &lt;span class="n"&gt;Hooks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Onload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My plugin!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lifecycle hooks
&lt;/h2&gt;

&lt;p&gt;I didn't find any docs about the lifecycle hooks, so here is a little summary without any detail, and probably with some errors or omissions, from their file &lt;code&gt;server/common/plugin.go&lt;/code&gt;, part of the &lt;code&gt;common&lt;/code&gt; package.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ProcessFileContentBeforeSend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HttpEndpoint&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Static&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Starter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthenticationMiddleware&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthorisationMiddleware&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SearchEngine&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Thumbnailer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuditEngine&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FrontendOverrides&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;XDGOpen&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CSS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CSSFunc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Favicon&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Onload&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Middleware&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StaticPatch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Metadata&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WorkflowTrigger&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WorkflowAction&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just to be clear, I don't know yet the meaning, use case, or configuration of most of these, I am just listing them to keep them at hand. Some may be self-descriptive but a careful analysis of the code is recommended before using it for serious work.&lt;/p&gt;

</description>
      <category>filestash</category>
      <category>filemanager</category>
      <category>go</category>
      <category>plugin</category>
    </item>
    <item>
      <title>A Docker development environment for Filestash</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Mon, 13 Oct 2025 09:48:21 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/a-docker-development-environment-for-filestash-4m7n</link>
      <guid>https://dev.to/migsarnavarro/a-docker-development-environment-for-filestash-4m7n</guid>
      <description>&lt;h2&gt;
  
  
  What is Filestash?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.filestash.app/" rel="noopener noreferrer"&gt;Filestash&lt;/a&gt; is a web file manager that works with many different storage solutions. It provides a way to view, and often interact with the files from a web browser.&lt;/p&gt;

&lt;p&gt;I was recently looking for an open-source solution to interact with s3 storage and some AI powered browser suggested Filestash, while it was not exactly what I was looking for I decided to give it a try.&lt;/p&gt;

&lt;p&gt;I was pleasantly surprised about its simplicity, and it actually worked out-of-the-box, locally, for my very simple use case. Then I started exploring how to actually deploy it somewhere publicly accessible and that's when the problems started, sort of...&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do I need a dev environment?
&lt;/h2&gt;

&lt;p&gt;The preconfigured image works very well for a simple scenario, and it can easily be used locally, but then you may realize it has many things you don't need and it may not have some of the things you want. To customize your instance is a bit more difficult since documentation is not updated and some things that appear in the official docs are not actually open-source, and only available for paid customers. That being said, I totally understand the reason for those decisions, particularly nowadays.&lt;/p&gt;

&lt;p&gt;So Filestash may not be the best solution if you need something quick that just works, but I like the philosophy behind it, even if I am not in love with the architecture, so I think it is worth to invest a little time exploring and, ideally, supporting the project.&lt;/p&gt;

&lt;p&gt;We need a dev-environment even to do little changes like adding or removing plugins, but also to get a better idea of how it works and what can be done.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple development environment
&lt;/h2&gt;

&lt;p&gt;Since Filestash already provided a docker image I thought it would be very straightforward for me and other users to move in that direction, also I am not a fan of local development, not for my regular tools, but even less if the development is in a language I don't use that often.&lt;/p&gt;

&lt;p&gt;The image provided in Filestash repository has a two problems for development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It uses several stages. That makes sense for deployment, but not for development.&lt;/li&gt;
&lt;li&gt;It clones the repository instead of using the local files, so it is harder to edit and compile to see the changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the Containerfile I used, I actually used &lt;a href="https://podman.io/" rel="noopener noreferrer"&gt;podman&lt;/a&gt; instead of docker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; docker.io/golang:1.25.2-trixie&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl make &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libjpeg-dev libtiff-dev libpng-dev libwebp-dev libraw-dev libheif-dev libgif-dev libvips-dev &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8334&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see I didn't copy the files, I just used the latest golang image but referred to it by full version to avoid problems later when it is not latest anymore, and installed all the dependencies required in the original file, I installed the ones in the compile step which are not the same from the running container, for example, I didn't install &lt;code&gt;ffmpeg&lt;/code&gt;, since it is not a feature I care about.&lt;/p&gt;

&lt;p&gt;Then to build the image use:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;podman build -t filestash-dev:20251013 -f docker/Containerfile.filestash-dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There are two things to notice. First, I prefer to use the date in the versioning of my dev images but it can be named and versioned at will, second, the same applies for the &lt;code&gt;Containerfile&lt;/code&gt; it is just a matter of personal preference.&lt;/p&gt;

&lt;p&gt;Now we can run the service, for that the following line is used:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;podman run -it -v .:/app -p 8334:8334 filestash-dev:20251013 /bin/bash&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Again, there are a few important things to discuss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current local directory is mounted in &lt;code&gt;/app&lt;/code&gt;, it is the same as the used in &lt;code&gt;WORKDIR&lt;/code&gt; for convenience. The output will be created in the same directory, so it will exist even after the container is destroyed, but the cache used to build won't.&lt;/li&gt;
&lt;li&gt;We need to include the port in which our service will run so it is reachable outside of the container.&lt;/li&gt;
&lt;li&gt;I executed &lt;code&gt;/bin/bash&lt;/code&gt; so I can run the commands in the terminal inside the container. I actually use VSCode to edit the code but locally, not inside the container. These days there are many ways to do it and container integration in VSCode is great, but it is up to you to decide the best workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Afterthoughts
&lt;/h2&gt;

&lt;p&gt;This is just a very quick and simple configuration, there are some shortcomings, at least for me, like being able to keep the bash history, which is not hard to do using container volumes, but it is a good starting point that leaves things open enough to suit several workflows. &lt;/p&gt;

</description>
      <category>filestash</category>
      <category>s3</category>
      <category>container</category>
      <category>filemanager</category>
    </item>
    <item>
      <title>Change the default application for Ubuntu's terminal launcher</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Sat, 29 Apr 2023 20:04:21 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/change-the-default-application-for-ubuntus-terminal-launcher-13pc</link>
      <guid>https://dev.to/migsarnavarro/change-the-default-application-for-ubuntus-terminal-launcher-13pc</guid>
      <description>&lt;p&gt;This is something I has been trying to accomplish for a long time, maybe six months now. I started looking for it, then find a dead end and leave it untouched for a few weeks. This morning I decided it was distracting me enough so I really have to find it. It ended up being simpler than I thought, sadly the answer is not obvious and there are so many misleading pages out there.&lt;/p&gt;

&lt;p&gt;About a year ago I bought an amazing laptop, a tuxedo computer with their recommended OS installed, I must say I am quite happy with the laptop, and definitely think it is worth it. It came with Tilix preinstalled as the terminal, which is really nice I must admit, and I would love to explore at some point in the future (no commitment here), but the moment I learned about &lt;a href="https://alacritty.org/" rel="noopener noreferrer"&gt;Alacritty&lt;/a&gt; I fell in love with it.&lt;/p&gt;

&lt;p&gt;So I am used to Ubuntu's &lt;code&gt;&amp;lt;Primary&amp;gt;&amp;lt;Alt&amp;gt;t&lt;/code&gt; to open the terminal, and from time to time I use the &lt;em&gt;Open in Terminal&lt;/em&gt; from the file explorer and I wanted to properly configure my system, with no overrides or symbolic links to use Alacritty instead of Tilix.&lt;/p&gt;

&lt;p&gt;After reading a few StackOverflow answers I learned about &lt;em&gt;dconf&lt;/em&gt;, I was able to find where to change the shortcut to trigger the action but not how to change the command that was executed by the action, and I was stuck with that problem for a few weeks. Then at some point I read about:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gsettings list-recursively&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you pass no argument it lists all the keys and values. I didn't think about it initially but my answer ended up coming from here. I saved the output of the command in a text file, opened it in vim and search for tilix.&lt;/p&gt;

&lt;p&gt;It was that way that I found this four keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org.gnome.desktop.default-applications.terminal exec-arg '-e'
org.gnome.desktop.default-applications.terminal exec 'tilix'
org.cinnamon.desktop.default-applications.terminal exec-arg '-x'
org.cinnamon.desktop.default-applications.terminal exec 'tilix'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I did was totally empirical, so if someone can shed some light about the related concepts I would really appreciate it. No need to install &lt;code&gt;update-alternatives&lt;/code&gt; or to alter my system in any other form, just used &lt;code&gt;gsettings&lt;/code&gt; to set these four keys and it worked. Since I've no idea if I needed any argument I just set that empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gsettings set org.gnome.desktop.default-applications.terminal exec-arg ""
gsettings set org.gnome.desktop.default-applications.terminal exec "alacritty"
gsettings set org.cinnamon.desktop.default-applications.terminal exec-arg ""
gsettings set org.cinnamon.desktop.default-applications.terminal exec "alacritty"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! It worked like charm! Hope it save you some frustration :)!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ubuntu</category>
      <category>terminal</category>
      <category>dconf</category>
    </item>
    <item>
      <title>Use xclip to copy a file to clipboard</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Sat, 29 Apr 2023 19:24:19 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/use-xclip-to-copy-a-file-to-clipboard-2okn</link>
      <guid>https://dev.to/migsarnavarro/use-xclip-to-copy-a-file-to-clipboard-2okn</guid>
      <description>&lt;p&gt;This is a very brief post that I want to write here and maybe I'll try to expand later.&lt;/p&gt;

&lt;p&gt;I have tried in the past to copy text from a file into the clipboard to paste it in a gist at Github. The easiest way for someone not familiar with the command line is to simply open the file in a regular graphical editor, then select all the text and finally copy it from there.&lt;/p&gt;

&lt;p&gt;The thing is, that is not very straightforward, right?&lt;/p&gt;

&lt;p&gt;I must admit I've not read the man page for &lt;code&gt;xclip&lt;/code&gt; but also, man pages are usually exhaustive but not very concise when you are trying to solve a problem. After taking a look at a few StackOverflow answers and the man page I realize this can be done in a very simple way:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;xclip -selection clipboard -i script.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There are two nice things to notice here that once you know make &lt;code&gt;xclip&lt;/code&gt; very flexible:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-selection&lt;/code&gt; it can be "primary" (default), "secondary", or "clipboard". Apparently it is not 100% standardized but it is fairly safe to assume that "clipboard" will work in most cases for browser based apps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-i&lt;/code&gt; you don't even have to select the text but you can immediately upload the whole file. For me this is perfect. But I admit there are cases in which selecting some parts would work better.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There seem to be some other very interesting options in the man page, but that will have to wait for later.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>xclip</category>
      <category>clipboard</category>
    </item>
    <item>
      <title>Launch AppImage applications from desktop</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Fri, 21 Oct 2022 06:31:48 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/launch-appimage-applications-from-desktop-579</link>
      <guid>https://dev.to/migsarnavarro/launch-appimage-applications-from-desktop-579</guid>
      <description>&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@cr0t" rel="noopener noreferrer"&gt;Sergey Kuznetsov&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are at least three ways of distributing applications in Linux: AppImage, Flatpak, and Snap. Each one of these have different advantages and disadvantages but often it is not that important as a user since the format to use is a decision to be taken by the developer. Some applications already take care of this step for you, in case they don't keep reading, in this post I will show you a way to configure &lt;a href="https://appimage.org/" rel="noopener noreferrer"&gt;AppImage&lt;/a&gt; applications to be available from your desktop.&lt;/p&gt;

&lt;p&gt;First I want to say I am not an expert in the subject, it is just a problem I had been facing for some time now and today I decided to spend some time putting the pieces together and documenting my solution, if you happen to know a bit more about the topic I encourage you to leave a note in the comments and I'll be happy to update. First some questions you will probably need to answer at some point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to put the app file?
&lt;/h2&gt;

&lt;p&gt;This is totally opinionated, my only important recommendation is to move them out of the &lt;code&gt;~/Downloads&lt;/code&gt;, &lt;code&gt;~/Desktop&lt;/code&gt; or &lt;code&gt;~/Documents&lt;/code&gt; common folders. I like to use &lt;code&gt;/opt&lt;/code&gt;, since I believe it is a nice way to keep things in a place I know I have to look for when moving my things from one computer to another.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to find mime types?
&lt;/h2&gt;

&lt;p&gt;If you happen to need a mime type for the configuration, you can use this command that will be available in most Linux distros,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;xdg-mime query filetype README.md&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For a more detailed explanation read &lt;a href="https://www.linuxshelltips.com/find-file-mime-type-linux/" rel="noopener noreferrer"&gt;How to Determine MIME Type of a File in Linux&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How desktop environments find apps?
&lt;/h2&gt;

&lt;p&gt;Fortunately for us, the users, desktop environments like GNOME adopted a standard configuration file to describe how an application will be displayed in the start menu and later launched, you can read about it in the latest &lt;a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/" rel="noopener noreferrer"&gt;Desktop Entry Specification&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where are those files located?
&lt;/h3&gt;

&lt;p&gt;One common place to look for these files is &lt;code&gt;/usr/share/applications/&lt;/code&gt;, it is good for learning and looking for examples of categories, but probably not a good idea to put your files there. Apparently the recommendation is to use &lt;code&gt;~/.local/share/applications/&lt;/code&gt; where &lt;code&gt;~&lt;/code&gt; stand for your home directory. This recommendation makes sense because it is usually better to have system configuration you made contained in your own directory.&lt;/p&gt;

&lt;p&gt;Inside of these directories you will find a dozen of files using the desktop extension. Those are just text files that describe how to display and launch your applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does a .desktop file looks like?
&lt;/h3&gt;

&lt;p&gt;As I mentioned above, there is a full specification, but for the sake of learning, I'll show here the file I created for &lt;a href="https://www.beekeeperstudio.io/" rel="noopener noreferrer"&gt;Beekeeper Studio&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Desktop Entry]
Name=Beekeeper Studio
Comment=Modern and easy to use SQL client for MySQL, Postgres, SQLite, SQL Server, and more.
TryExec=/opt/Beekeeper-Studio-3.5.1.AppImage
Exec=/opt/Beekeeper-Studio-3.5.1.AppImage
Icon=beekeeper-studio
Terminal=false
StartupWMClass=Beekeeper
Type=Application
Categories=Development;
Keywords=sql;db;database;
X-GNOME-UsesNotifications=true
X-GNOME-SingleWindow=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be honest I don't know if all lines are valid, I used another file as a template and left most properties that seemed to make sense.&lt;/p&gt;

&lt;p&gt;One important note is that the &lt;code&gt;Icon&lt;/code&gt; refers to an icon I downloaded from the project's Github repository and placed in &lt;code&gt;~/.local/share/icons/beekeeper-studio.png&lt;/code&gt;, I saw in StackOverflow that svg seemed to be valid, but didn't try that. About the &lt;code&gt;Category&lt;/code&gt; I just copied it from another entry as well, but in &lt;a href="https://askubuntu.com/questions/674403/when-creating-a-desktop-file-what-are-valid-categories" rel="noopener noreferrer"&gt;this answer&lt;/a&gt; you can read a bit more about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not use an automated method?
&lt;/h2&gt;

&lt;p&gt;There seem to be two ways of doing this automatically and are explained in AppImage's documentation for &lt;a href="https://docs.appimage.org/user-guide/run-appimages.html#integrating-appimages-into-the-desktop" rel="noopener noreferrer"&gt;desktop integration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Probably those are better suited, but I was looking for something very straight-forward and the description there was not something like copy &amp;amp; paste solution, it implied more reading, and after seeing that Alacritty &lt;a href="https://github.com/alacritty/alacritty/blob/master/extra/linux/Alacritty.desktop" rel="noopener noreferrer"&gt;included the desktop file&lt;/a&gt; I thought it was way easier to just type a few lines in a text file instead of reading documentation and installing another thing in my system.&lt;/p&gt;




&lt;p&gt;While I was looking for how to do it, I came to some YouTube videos, while I am not much of a video guy I did use some of the description as confirmation of what I was doing, and I think it is probably worth watching them, so here are the links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=bXZma0t0PKE" rel="noopener noreferrer"&gt;How to use AppImages and Integrate them into your system&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/bXZma0t0PKE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=OftD86RgAcc" rel="noopener noreferrer"&gt;HUGE Speed Differences! Flatpak vs. Snap vs. AppImage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/OftD86RgAcc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you found this information useful, please give it a like to let me know you care and continue writing. You can follow me here or in &lt;a href="https://twitter.com/MigsarNavarro" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to keep you informed when I write a new post.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>appimage</category>
      <category>freedesktop</category>
      <category>launcher</category>
    </item>
    <item>
      <title>Installing Alacritty without installing Rust</title>
      <dc:creator>Migsar Navarro</dc:creator>
      <pubDate>Tue, 21 Jun 2022 18:44:20 +0000</pubDate>
      <link>https://dev.to/migsarnavarro/installing-alacritty-without-installing-rust-2l94</link>
      <guid>https://dev.to/migsarnavarro/installing-alacritty-without-installing-rust-2l94</guid>
      <description>&lt;p&gt;A week ago I came to the &lt;a href="https://alacritty.org/" rel="noopener noreferrer"&gt;Alacritty's project homepage&lt;/a&gt;, again, and I wanted to install it, again, but was discouraged by reading that I had to build it.&lt;/p&gt;

&lt;p&gt;I used to build things back in the days (I won't say I was really into compiling things from source, but I am old enough to have lived in a world before i , but more and more I prefer the convenience of just installing things without having to install all the dev tools in my machine.&lt;/p&gt;

&lt;p&gt;In Rust's Docker image description you can find a section entitled &lt;a href="https://github.com/docker-library/docs/tree/master/rust#compile-your-app-inside-the-docker-container" rel="noopener noreferrer"&gt;Compile your app inside the Docker container&lt;/a&gt;, which is precisely what I wanted to do, but there will be a problem since two &lt;code&gt;lxcb&lt;/code&gt; libraries are not included:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  = note: /usr/bin/ld: cannot find -lxcb-shape
          /usr/bin/ld: cannot find -lxcb-xfixes
          collect2: error: ld returned 1 exit status

error: could not compile `alacritty` due to previous error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;I had already started with this, so I'll depart a bit for some config, but it is essentially the same.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instead of using the image to run cargo directly open a shell console.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:/backup rust /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Update sources and install lxcb dev libraries.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# You are already root inside the container&lt;/span&gt;
apt-get update

&lt;span class="c"&gt;# This is the only lib missing from Alacritty's build pre-requisites&lt;/span&gt;
apt-get &lt;span class="nb"&gt;install &lt;/span&gt;libxcb-xfixes0-dev

&lt;span class="c"&gt;# Now compile, you can use the more concise command from Rust's image description&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;alacritty
&lt;span class="c"&gt;# I mounted my home dir as /backup&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; /usr/local/cargo/bin/alacritty /backup/alacritty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Outside of the container you need to copy &lt;code&gt;alacritty&lt;/code&gt; to some directory included in your &lt;code&gt;PATH&lt;/code&gt;, I used:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mv &lt;/span&gt;alacritty /usr/local/bin/alacritty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;One final notice is that, as stated in the &lt;a href="https://github.com/alacritty/alacritty/blob/master/INSTALL.md" rel="noopener noreferrer"&gt;build instructions&lt;/a&gt;, this won't install  the terminfo file, desktop entry, manual page or shell completions, but shouldn't be hard to get that working.&lt;/p&gt;

</description>
      <category>alacritty</category>
      <category>rust</category>
      <category>terminal</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
