<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Andrea Grandi</title>
        <link>https://www.andreagrandi.it/</link>
        <description>Recent content on Andrea Grandi</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <lastBuildDate>Sat, 14 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.andreagrandi.it/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Releasing mcp-wire: a tool to easily configure MCPs in coding agents</title>
        <link>https://www.andreagrandi.it/posts/releasing-mcp-wire-tool-easily-configure-mcps-coding-agents/</link>
        <pubDate>Sat, 14 Feb 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/releasing-mcp-wire-tool-easily-configure-mcps-coding-agents/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/releasing-mcp-wire-tool-easily-configure-mcps-coding-agents/mcp-wire-logo.png" alt="Featured image of post Releasing mcp-wire: a tool to easily configure MCPs in coding agents" /&gt;&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;If you&amp;rsquo;ve been using AI coding agents like Claude Code, OpenCode, or Codex, you&amp;rsquo;ve probably noticed that each one has its own way of configuring MCP (Model Context Protocol) servers. Claude Code uses JSON, OpenCode uses JSONC, Codex uses TOML, and they all store things in different locations. Adding a new MCP service means manually editing the right config file for each tool you use.&lt;/p&gt;
&lt;p&gt;I found myself repeating this process every time I wanted to add or update an MCP server, and it got old really fast.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/mcp-wire&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;mcp-wire&lt;/strong&gt;&lt;/a&gt; is a CLI tool that does all of this for you. It installs and configures MCP servers across multiple AI coding tools from a single, guided interface.&lt;/p&gt;
&lt;h2 id=&#34;why-mcp-wire&#34;&gt;Why mcp-wire?
&lt;/h2&gt;&lt;p&gt;I use multiple coding agents depending on the task, and I want them all to have access to the same MCP services. Manually editing JSON, JSONC, and TOML config files across different tools is tedious and error-prone. I wanted a single command that would:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detect which coding tools I have installed&lt;/li&gt;
&lt;li&gt;Let me pick an MCP service to install&lt;/li&gt;
&lt;li&gt;Handle credentials&lt;/li&gt;
&lt;li&gt;Write the correct configuration to each tool&amp;rsquo;s config file&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So I built mcp-wire to do exactly that.&lt;/p&gt;
&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works
&lt;/h2&gt;&lt;p&gt;Run &lt;code&gt;mcp-wire&lt;/code&gt; and you get a guided menu:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mcp-wire
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Use Up/Down arrows, Enter to select.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; Main Menu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Install service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Uninstall service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Status
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  List services
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  List targets
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Exit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The install wizard walks you through the whole process step by step: pick a service, select one or more targets, review, and apply. No config files to find or edit by hand.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mcp-wire install
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Install Wizard
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Step 1/4: Service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Use Up/Down arrows, Enter to select. Type to filter.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; Select service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  jira - Atlassian Rovo MCP server (OAuth)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Step 2/4: Targets
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Detected targets:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Claude Code (claude) [installed]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  OpenCode (opencode) [installed]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Codex (codex) [installed]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[ ] Claude Code (claude)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[x] OpenCode (opencode)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[ ] Codex (codex)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Step 3/4: Review
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Service: jira
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Targets: OpenCode
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Credentials: prompt as needed
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Step 4/4: Apply
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Installing to: OpenCode
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  OpenCode: configured
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Equivalent command: mcp-wire install jira --target opencode
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;If you prefer one-liners over menus, every action has a direct command equivalent:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mcp-wire install jira --target claude --scope project
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mcp-wire uninstall sentry --target opencode
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mcp-wire status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;supported-targets&#34;&gt;Supported Targets
&lt;/h2&gt;&lt;p&gt;mcp-wire currently supports three coding agents:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude Code&lt;/strong&gt; (&lt;code&gt;claude&lt;/code&gt;) - with user and project scope support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codex&lt;/strong&gt; (&lt;code&gt;codex&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenCode&lt;/strong&gt; (&lt;code&gt;opencode&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It automatically detects which ones are installed on your system and only shows those as available targets.&lt;/p&gt;
&lt;h2 id=&#34;scope-aware-installs&#34;&gt;Scope-Aware Installs
&lt;/h2&gt;&lt;p&gt;For Claude Code, mcp-wire supports scoped installations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;user&lt;/strong&gt; (default): MCP service is available across all projects&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;project&lt;/strong&gt;: MCP service is only available for the current project&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mcp-wire install jira --target claude --scope user
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mcp-wire install jira --target claude --scope project
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mcp-wire status --scope effective
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;effective&lt;/code&gt; scope shows you the merged result of user and project configurations, so you can see exactly what&amp;rsquo;s active.&lt;/p&gt;
&lt;h2 id=&#34;bundled-services&#34;&gt;Bundled Services
&lt;/h2&gt;&lt;p&gt;mcp-wire comes with a few services out of the box:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;context7&lt;/strong&gt; - Context7 documentation lookup MCP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jira&lt;/strong&gt; - Atlassian Rovo MCP server&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sentry&lt;/strong&gt; - Sentry MCP server&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;playwright&lt;/strong&gt; - Playwright browser automation MCP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can list what&amp;rsquo;s available with &lt;code&gt;mcp-wire list services&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;adding-new-services&#34;&gt;Adding New Services
&lt;/h2&gt;&lt;p&gt;One thing I&amp;rsquo;m particularly happy about is how easy it is to add new services. You don&amp;rsquo;t need to write any Go code — just create a YAML file in the &lt;code&gt;services/&lt;/code&gt; directory:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;example&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Example MCP&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;transport&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;auth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;oauth&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mcp.example.com/mcp&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;For local command-based MCP servers (&lt;code&gt;stdio&lt;/code&gt; transport):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;example-stdio&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Example local MCP&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;transport&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;stdio&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;npx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;-y&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;@example/mcp-server&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This is also the easiest way to contribute to the project: adding service definitions for MCP servers you use.&lt;/p&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation
&lt;/h2&gt;&lt;p&gt;Install via Homebrew (macOS/Linux):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew tap andreagrandi/tap
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install mcp-wire
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Or build from source:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/andreagrandi/mcp-wire
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; mcp-wire
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make build
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./bin/mcp-wire
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Complete source code is available on GitHub: &lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/mcp-wire&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/andreagrandi/mcp-wire&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;mcp-wire is a small tool that scratches a very specific itch: keeping MCP configurations consistent across multiple coding agents without the manual toil. If you use more than one AI coding tool and you&amp;rsquo;re tired of editing config files by hand, give it a try.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s open source and contributions are welcome, especially new service definitions. If you have feedback or run into issues, feel free to &lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/mcp-wire/issues&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;open an issue&lt;/a&gt; or send a pull request 🙂&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Using VibeTunnel to control Claude Code instances remotely</title>
        <link>https://www.andreagrandi.it/posts/using-vibetunnel-to-control-claude-code-instances-remotely/</link>
        <pubDate>Sun, 25 Jan 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/using-vibetunnel-to-control-claude-code-instances-remotely/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/using-vibetunnel-to-control-claude-code-instances-remotely/claude_from_browser.png" alt="Featured image of post Using VibeTunnel to control Claude Code instances remotely" /&gt;&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;If you work with &lt;strong&gt;Claude Code&lt;/strong&gt; regularly, you know how useful it can be to have multiple instances running on different machines. But what happens when you&amp;rsquo;re away from your desk and need to check on a long-running task or provide input to your agent?&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://vibetunnel.sh/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;VibeTunnel&lt;/strong&gt;&lt;/a&gt; solves this problem by exposing your Claude Code terminal sessions through a web interface, allowing you to control them remotely from any browser.&lt;/p&gt;
&lt;h2 id=&#34;what-is-vibetunnel&#34;&gt;What is VibeTunnel?
&lt;/h2&gt;&lt;p&gt;VibeTunnel is a tool that transforms your browser into a functional terminal interface, enabling remote command execution with minimal setup. It runs as a server on your machine and provides a web dashboard where you can view and interact with active terminal sessions.&lt;/p&gt;
&lt;p&gt;On MacOS, it also comes with a menu bar application that shows the status of your tunnels and gives you quick access to open any active session in your browser.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;vibetunnel_status.png&#34; &gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/using-vibetunnel-to-control-claude-code-instances-remotely/vibetunnel_status.png&#34;
	width=&#34;384&#34;
	height=&#34;246&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;VibeTunnel status from the menu bar&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;156&#34;
		data-flex-basis=&#34;374px&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;some-use-cases&#34;&gt;Some Use Cases
&lt;/h2&gt;&lt;p&gt;There are several scenarios where VibeTunnel shines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Long-running tasks&lt;/strong&gt;: Start a complex refactoring or code generation task before leaving your desk, then check on its progress from your phone or another computer&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-machine workflows&lt;/strong&gt;: Control Claude Code instances running on your home workstation from your laptop at a coffee shop&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team collaboration&lt;/strong&gt;: Share your terminal session with a colleague to debug an issue together&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On-call scenarios&lt;/strong&gt;: Respond to production issues by accessing your development environment from anywhere&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-to-install-and-configure-it&#34;&gt;How to Install and Configure It
&lt;/h2&gt;&lt;h3 id=&#34;installation&#34;&gt;Installation
&lt;/h3&gt;&lt;p&gt;On &lt;strong&gt;MacOS&lt;/strong&gt; (Apple Silicon, MacOS 14.0+), you can install VibeTunnel via Homebrew:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install --cask vibetunnel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;After installation, launch the application from your Applications folder. You&amp;rsquo;ll see a new icon appear in your menu bar.&lt;/p&gt;
&lt;p&gt;Alternatively, on &lt;strong&gt;any platform&lt;/strong&gt; with Node.js 22.12+, you can install it via npm:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install -g vibetunnel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Or run it directly without installation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npx -y vibetunnel --no-auth
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;usage&#34;&gt;Usage
&lt;/h3&gt;&lt;p&gt;Once VibeTunnel is running, you use the &lt;code&gt;vt&lt;/code&gt; command to wrap any terminal command you want to monitor remotely. For Claude Code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vt claude
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This will start Claude Code as usual, but now it&amp;rsquo;s accessible through the web interface at &lt;code&gt;http://localhost:4020&lt;/code&gt;. The menu bar icon will update to show the active session.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;claude_terminal.png&#34; &gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/using-vibetunnel-to-control-claude-code-instances-remotely/claude_terminal.png&#34;
	width=&#34;1169&#34;
	height=&#34;846&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Claude Code terminal running through VibeTunnel&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;138&#34;
		data-flex-basis=&#34;331px&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can then open the dashboard in your browser to view and interact with your session:&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;claude_from_browser.png&#34; &gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/using-vibetunnel-to-control-claude-code-instances-remotely/claude_from_browser.png&#34;
	width=&#34;1496&#34;
	height=&#34;815&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Claude Code visible in the browser through VibeTunnel&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;183&#34;
		data-flex-basis=&#34;440px&#34;
	
&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you need to bind to a network interface for remote access:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vibetunnel --bind 0.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;not-just-claude-code&#34;&gt;Not Just Claude Code
&lt;/h2&gt;&lt;p&gt;While I&amp;rsquo;ve focused on Claude Code in this post, VibeTunnel works with any terminal application. You can use it to remotely control:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Other AI coding agents&lt;/strong&gt;: Gemini CLI, OpenAI Codex, Aider, or any other agent that runs in the terminal&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive shells&lt;/strong&gt;: Start a full zsh or bash session with &lt;code&gt;vt --shell&lt;/code&gt; and have remote access to your entire development environment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Long-running scripts&lt;/strong&gt;: Monitor build processes, test suites, or deployment scripts from anywhere&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Development servers&lt;/strong&gt;: Keep an eye on your local dev server logs while away from your desk&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Start Gemini CLI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vt gemini
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Start an interactive shell session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vt --shell
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Run and monitor a build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vt npm run build
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Watch test output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vt pytest -v
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Essentially, if it runs in a terminal, you can wrap it with &lt;code&gt;vt&lt;/code&gt; and access it remotely.&lt;/p&gt;
&lt;h2 id=&#34;using-tailscale-to-make-instances-reachable-from-everywhere&#34;&gt;Using Tailscale to Make Instances Reachable from Everywhere
&lt;/h2&gt;&lt;p&gt;By default, VibeTunnel only exposes your sessions on localhost. To access them from anywhere, you can combine it with &lt;a class=&#34;link&#34; href=&#34;https://tailscale.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Tailscale&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Tailscale creates a secure, private network between your devices. Once you have it set up:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install Tailscale on your development machine and any device you want to access it from&lt;/li&gt;
&lt;li&gt;Make sure both devices are connected to your Tailnet&lt;/li&gt;
&lt;li&gt;Start VibeTunnel with network binding: &lt;code&gt;vibetunnel --bind 0.0.0.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Access your VibeTunnel dashboard at &lt;code&gt;http://[your-tailscale-hostname]:4020&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This way, you can securely access your Claude Code instances from your phone, tablet, or any other computer, regardless of where you are in the world.&lt;/p&gt;
&lt;h2 id=&#34;what-could-be-improved&#34;&gt;What Could Be Improved
&lt;/h2&gt;&lt;p&gt;While VibeTunnel is a useful tool, there are a few areas where it could be better:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mobile usability&lt;/strong&gt;: The web interface works on mobile browsers, but it&amp;rsquo;s not optimised for smaller screens. Typing commands and reading output on a phone can be awkward&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Touch interface&lt;/strong&gt;: Terminal interactions don&amp;rsquo;t translate well to touch, making it difficult to do anything beyond basic monitoring on mobile devices&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If mobile access is a priority for you, you might want to check out &lt;a class=&#34;link&#34; href=&#34;https://happy.engineering/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Happy&lt;/strong&gt;&lt;/a&gt; as an alternative. It provides a more mobile-friendly interface for interacting with your coding agents remotely (but it is more limited in terms of features and compatibility, ie using /commands etc&amp;hellip;)&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;VibeTunnel is a handy tool for anyone who wants more flexibility in how they interact with Claude Code. Combined with Tailscale, it opens up the possibility of truly remote development workflows.&lt;/p&gt;
&lt;p&gt;While the mobile experience could use some polish, it&amp;rsquo;s already useful for monitoring long-running tasks and occasionally providing input when you&amp;rsquo;re away from your main machine.&lt;/p&gt;
&lt;p&gt;If you give it a try, I&amp;rsquo;d be curious to hear about your experience!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Sentire - a command line interface for Sentry API</title>
        <link>https://www.andreagrandi.it/posts/sentire-command-line-interface-sentry-api/</link>
        <pubDate>Sat, 30 Aug 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/sentire-command-line-interface-sentry-api/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/sentire-command-line-interface-sentry-api/sentire.png" alt="Featured image of post Sentire - a command line interface for Sentry API" /&gt;&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;When I use coding agents, I often need to retrieve details from a Sentry issue to provide context for diagnosing the root cause. I’ve been using the Sentry MCP for this, but I wanted to switch to a simpler command-line tool. I assumed &lt;code&gt;sentry-cli&lt;/code&gt; would fit the bill, until I realized it’s designed for a different purpose (per their docs):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;It’s primarily used for managing debug information files for iOS, 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Android as well as release and source maps management for other platforms.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;At this point I realised I needed a different CLI. I tried to search around but I couldn&amp;rsquo;t find exactly what I was looking for, so I decided to give it a shot.&lt;/p&gt;
&lt;h2 id=&#34;what-is-sentire&#34;&gt;What is Sentire?
&lt;/h2&gt;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/sentire&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Sentire&lt;/a&gt; is a simple and user-friendly command line interface for the Sentry API, written in Go. The name &amp;ldquo;sentire&amp;rdquo; comes from Italian, meaning &amp;ldquo;to feel&amp;rdquo; - reflecting its purpose of helping developers feel the pulse of their application&amp;rsquo;s health through Sentry&amp;rsquo;s monitoring data.&lt;/p&gt;
&lt;p&gt;Sentire provides an intuitive CLI for interacting with Sentry&amp;rsquo;s API, covering essential operations including managing events, issues, projects, and organizations.&lt;/p&gt;
&lt;h2 id=&#34;key-features&#34;&gt;Key Features
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multiple Output Formats&lt;/strong&gt;: JSON, table, text, and markdown formats to suit different use cases&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comprehensive API Coverage&lt;/strong&gt;: Essential Sentry operations for debugging workflows&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in Pagination and Rate Limiting&lt;/strong&gt;: Handles Sentry&amp;rsquo;s API constraints automatically&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Human-readable Output&lt;/strong&gt;: Perfect for terminal usage and quick analysis&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Machine-readable JSON&lt;/strong&gt;: Ideal for scripting and automation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URL Inspection&lt;/strong&gt;: Parse Sentry URLs directly to get detailed event information&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this CLI doesn&amp;rsquo;t implement (yet) all the Sentry API. I focussed on the commands needed to retrieve projects, issues and events. More commands will be added in future releases.&lt;/p&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation
&lt;/h2&gt;&lt;p&gt;Install via Homebrew (macOS):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew tap andreagrandi/tap
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install sentire
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;For other installation methods (Go install, pre-built binaries, building from source), see the &lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/sentire#installation&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;official README&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Complete &lt;strong&gt;source code&lt;/strong&gt; is available on GitHub: &lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/sentire&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/andreagrandi/sentire&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;getting-started&#34;&gt;Getting Started
&lt;/h2&gt;&lt;p&gt;First, set your Sentry API token as an environment variable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SENTRY_API_TOKEN&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_sentry_api_token_here
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can obtain an API token from your Sentry organization settings under &amp;ldquo;Auth Tokens&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Then you can start exploring your projects and issues:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# List all your projects&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sentire projects list
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Get a specific project&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sentire projects get &amp;lt;organization&amp;gt; &amp;lt;project&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# List recent issues for an organization&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sentire events list-issues &amp;lt;organization&amp;gt; --query&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;is:unresolved&amp;#34;&lt;/span&gt; --period&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;24h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Get detailed information about a specific issue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sentire events get-issue &amp;lt;organization&amp;gt; &amp;lt;issue-id&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;output-formats&#34;&gt;Output Formats
&lt;/h2&gt;&lt;p&gt;Sentire supports multiple output formats to suit different workflows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JSON&lt;/strong&gt; (default): Machine-readable format for scripting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Table&lt;/strong&gt;: Human-readable format with borders for terminal viewing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text&lt;/strong&gt;: Clean plain text format for simple parsing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Markdown&lt;/strong&gt;: Documentation-friendly format for reports&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Human-readable table format&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sentire events list-issues my-org --format table
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Markdown format for documentation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sentire org stats my-org --format markdown
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;url-inspection&#34;&gt;URL Inspection
&lt;/h2&gt;&lt;p&gt;One of Sentire&amp;rsquo;s unique features is the ability to parse Sentry URLs directly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Inspect a Sentry issue URL and get detailed event information&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sentire inspect &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://my-org.sentry.io/issues/123456789/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This automatically extracts the organization and issue ID from the URL and fetches the most relevant debugging information.&lt;/p&gt;
&lt;h2 id=&#34;use-cases&#34;&gt;Use Cases
&lt;/h2&gt;&lt;p&gt;Sentire is particularly useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Debugging Workflows&lt;/strong&gt;: Quickly inspect Sentry URLs and get detailed event information without leaving the terminal&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DevOps Teams&lt;/strong&gt;: Integrating error monitoring into deployment pipelines with machine-readable JSON output&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On-call Engineers&lt;/strong&gt;: Using table format for quick issue triage and markdown format for incident reports&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Development Teams&lt;/strong&gt;: Multiple output formats reduce context switching between terminal and web interface&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automation and Scripting&lt;/strong&gt;: Built-in rate limiting and pagination make it reliable for automated workflows&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: Markdown output format perfect for including error analysis in reports and documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Sentire brings Sentry to your terminal because, let&amp;rsquo;s be honest, who has time to click around web interfaces when bugs are breathing down your neck?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s open source, so if you find bugs (oh, the irony!), have feature requests, or just want to make it less terrible, feel free to &lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/sentire/issues&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;open an issue&lt;/a&gt; or send a pull request. I hope you will enjoy it 🙂&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Claude Code Status Line Script</title>
        <link>https://www.andreagrandi.it/posts/claude-code-status-line-script/</link>
        <pubDate>Fri, 15 Aug 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/claude-code-status-line-script/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/claude-code-status-line-script/cc-statusline.png" alt="Featured image of post Claude Code Status Line Script" /&gt;&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;A few days ago, Anthropic released an update for Claude Code that adds an &lt;a class=&#34;link&#34; href=&#34;https://docs.anthropic.com/en/docs/claude-code/statusline&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;interesting feature&lt;/a&gt;: a new command named &lt;code&gt;/statusline&lt;/code&gt;, which will create a script to fetch and show useful information in the app’s status bar and automatically add the configuration for it.&lt;/p&gt;
&lt;p&gt;You can either let Claude generate the script for you, or provide your own.&lt;/p&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation
&lt;/h2&gt;&lt;p&gt;To install my script, you need to download the bash script from &lt;a class=&#34;link&#34; href=&#34;https://gist.github.com/andreagrandi/362c1fc77909b202476aba9b57cb0145&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;this location&lt;/a&gt; save it in &lt;code&gt;~/.claude/statusline-script.sh&lt;/code&gt;, make it executable with&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod +x ~/.claude/statusline-script.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;and add this configuration in &lt;code&gt;~/.claude/settings.json&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;statusLine&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;~/.claude/statusline-script.sh&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;features&#34;&gt;Features
&lt;/h2&gt;&lt;p&gt;As you can see from the above screenshot, the script will display the following information:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;📁 logbasset &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 🦫 1.24.5 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 🌿 10-logging &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 🤖 Opus 4.1 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 💸 &lt;span class=&#34;nv&#34;&gt;$0&lt;/span&gt;.00 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 💰 &lt;span class=&#34;nv&#34;&gt;$14&lt;/span&gt;.64/day
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;📁 current folder&lt;/li&gt;
&lt;li&gt;🦫 language version (in this case it&amp;rsquo;s a Go app but for Python it will show &lt;code&gt;💼 (my-app) | 🐍 3.12.9&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;🌿 git branch name&lt;/li&gt;
&lt;li&gt;🤖 current model&lt;/li&gt;
&lt;li&gt;💸 cost of the current session&lt;/li&gt;
&lt;li&gt;💰 total cost for the day&lt;/li&gt;
&lt;li&gt;⏱️ time left for the current session&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The app uses &lt;code&gt;ccusage&lt;/code&gt; to fetch costs.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;In the last few days there has been an explosion of scripts and small apps which can provide a status line for Claude Code. I decided to give it a try and do my custom one. I hope it can be useful for other people too 😉&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Releasing Log Basset: an open source CLI to access Scalyr logs</title>
        <link>https://www.andreagrandi.it/posts/releasing-log-basset-open-source-cli-access-scalyr-logs/</link>
        <pubDate>Mon, 28 Jul 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/releasing-log-basset-open-source-cli-access-scalyr-logs/</guid>
        <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;img src=&#34;logbasset.png&#34; alt=&#34;Log Basset: an open source CLI to access Scalyr logs&#34; width=&#34;20%&#34; /&gt;
&lt;p&gt;At work, we use &lt;strong&gt;Scalyr&lt;/strong&gt; to access our logs. They provide a web interface and a &lt;a class=&#34;link&#34; href=&#34;https://github.com/scalyr/scalyr-tool&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;client&lt;/a&gt; written in Python, but it hasn&amp;rsquo;t been updated in the last couple of years, and I&amp;rsquo;m not sure if it&amp;rsquo;s abandoned.&lt;/p&gt;
&lt;p&gt;As CLI coding agents become more popular, it&amp;rsquo;s important to have reliable CLI tools for accessing various services.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/logbasset&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Log Basset&lt;/strong&gt;&lt;/a&gt; is a CLI tool for accessing Scalyr logs, written from scratch in Go.&lt;/p&gt;
&lt;h2 id=&#34;why-log-basset&#34;&gt;Why Log Basset?
&lt;/h2&gt;&lt;p&gt;There are a few reasons why I decided to rewrite their tool in Go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I wanted to have fun 🙂&lt;/li&gt;
&lt;li&gt;A tool written in Go is easier to distribute and use (no need to set up a virtual environment)&lt;/li&gt;
&lt;li&gt;I was curious to stress test &lt;a class=&#34;link&#34; href=&#34;https://opencode.ai&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;OpenCode&lt;/strong&gt;&lt;/a&gt; and &lt;code&gt;sonnet-4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yes, this tool was mostly written using AI tools. If that&amp;rsquo;s a problem for you, please do not use it.&lt;/p&gt;
&lt;h2 id=&#34;key-features&#34;&gt;Key Features
&lt;/h2&gt;&lt;p&gt;Log Basset maintains most of the original features, but I focused on the &amp;ldquo;read-only&amp;rdquo; ones. In particular, you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;query&lt;/strong&gt;: Retrieve log data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;power-query&lt;/strong&gt;: Execute PowerQuery&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;numeric-query&lt;/strong&gt;: Retrieve numeric/graph data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;facet-query&lt;/strong&gt;: Retrieve common values for a field&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;timeseries-query&lt;/strong&gt;: Retrieve numeric/graph data from a timeseries&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;tail&lt;/strong&gt;: Provide a live &amp;rsquo;tail&amp;rsquo; of a log&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compared to the original version, it also offers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flexible time formats and output types&lt;/li&gt;
&lt;li&gt;Strong input validation&lt;/li&gt;
&lt;li&gt;Multi-layer configuration&lt;/li&gt;
&lt;li&gt;Cross-platform support and high performance&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation
&lt;/h2&gt;&lt;p&gt;You can either use one of the packages from the GitHub &lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/logbasset/releases&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;release page&lt;/a&gt; or:&lt;/p&gt;
&lt;h3 id=&#34;homebrew-macoslinux&#34;&gt;Homebrew (macOS/Linux)
&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew tap andreagrandi/tap
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install logbasset
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;from-source&#34;&gt;From Source
&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/andreagrandi/logbasset
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; logbasset
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;usage-examples&#34;&gt;Usage Examples
&lt;/h2&gt;&lt;p&gt;Once you have set your API key like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;scalyr_readlog_token&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;your-api-token-here&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can query logs like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Display the last 10 log records&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;logbasset query
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Display the last 100 log records, showing only timestamp, severity, and message&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;logbasset query --count&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; --columns&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;timestamp,severity,message&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Display the first 10 log records beginning at 3:00 PM today, from host100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;logbasset query &lt;span class=&#34;s1&#34;&gt;&amp;#39;$serverHost=&amp;#34;host100&amp;#34;&amp;#39;&lt;/span&gt; --start&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3:00 PM&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Display the last 1000 entries in CSV format&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;logbasset query &lt;span class=&#34;s1&#34;&gt;&amp;#39;$source=&amp;#34;accessLog&amp;#34;&amp;#39;&lt;/span&gt; --output&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;csv --columns&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;status,uriPath&amp;#39;&lt;/span&gt; --count&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Or tail them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Display a live tail of all log records&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;logbasset tail
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Display a live tail from a specific host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;logbasset tail &lt;span class=&#34;s1&#34;&gt;&amp;#39;$serverHost=&amp;#34;host100&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Display live tail with full record details&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;logbasset tail --output multiline
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;A complete guide is available in the &lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/logbasset/blob/master/README.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;project readme&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Log Basset is my attempt to make working with Scalyr logs a bit easier and more enjoyable. If you give it a try and have feedback or ideas, let me know—or even better, open an issue or PR on GitHub!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Comparing Claude Code vs OpenCode (and testing different models)</title>
        <link>https://www.andreagrandi.it/posts/comparing-claude-code-vs-opencode-testing-different-models/</link>
        <pubDate>Thu, 17 Jul 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/comparing-claude-code-vs-opencode-testing-different-models/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/comparing-claude-code-vs-opencode-testing-different-models/opencode.png" alt="Featured image of post Comparing Claude Code vs OpenCode (and testing different models)" /&gt;&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;In the past few days &lt;a class=&#34;link&#34; href=&#34;https://github.com/anthropics/claude-code&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Claude Code&lt;/strong&gt;&lt;/a&gt; had some reliability issues and was sometimes unavailable. I started looking for alternative solutions (for context: through my job, I already have paid subscriptions to &lt;a class=&#34;link&#34; href=&#34;https://github.com/features/copilot&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt;&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://chatgpt.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;ChatGPT&lt;/strong&gt;&lt;/a&gt;) but I wanted something that would better fit my workflow.&lt;/p&gt;
&lt;p&gt;While I initially used Copilot in &lt;strong&gt;VSCode&lt;/strong&gt;, in the last month I’ve mostly been using VSCode for small fixes, code reviews, and navigating the codebase (asking questions using &lt;code&gt;@workspace&lt;/code&gt; still beats any other tool I’ve tried so far). I was primarily using Claude Code (I have a &lt;strong&gt;Pro&lt;/strong&gt; subscription, which is usually enough for my needs).&lt;/p&gt;
&lt;p&gt;I discovered &lt;a class=&#34;link&#34; href=&#34;https://opencode.ai&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;OpenCode&lt;/strong&gt;&lt;/a&gt; and liked that it’s not tied to a single provider—it can be used with most existing subscriptions and API keys (in particular, &lt;strong&gt;you can use your existing Claude Code Pro/Max or GitHub Copilot subscription&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;Having recently worked on a task at work (which I admit it&amp;rsquo;s simple to medium difficulty), after work I decided to give the same task to Claude Code and OpenCode (used with different models) logged in with my GitHub Copilot subscription.&lt;/p&gt;
&lt;h2 id=&#34;the-task&#34;&gt;The Task
&lt;/h2&gt;&lt;p&gt;I won&amp;rsquo;t (and I can&amp;rsquo;t) go into specific details of the task, since it&amp;rsquo;s work stuff, but I can say it was about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;add a new field &lt;code&gt;new_field&lt;/code&gt; to &lt;code&gt;ExistingEntity&lt;/code&gt; entity&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;new_field&lt;/code&gt; to &lt;code&gt;ExistingModel&lt;/code&gt; model&lt;/li&gt;
&lt;li&gt;create an &lt;code&gt;alembic&lt;/code&gt; migration&lt;/li&gt;
&lt;li&gt;fix existing tests&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-results&#34;&gt;The results
&lt;/h2&gt;&lt;p&gt;Overall, the code produced by these models was correct (with one exception), but in a few cases I had to make some manual fixes or request a second iteration, specifying what needed to be corrected.&lt;/p&gt;
&lt;h3 id=&#34;claude-code&#34;&gt;Claude Code
&lt;/h3&gt;&lt;p&gt;Claude was, hands down, the best overall. The initial iteration had a couple of minor problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the field was marked as not nullable while I wanted it nullable, but I didn&amp;rsquo;t specify this initially so it couldn&amp;rsquo;t guess it correctly&lt;/li&gt;
&lt;li&gt;the default value given to the field was quite &amp;ldquo;odd&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;opencode-copilotsonnet-4&#34;&gt;OpenCode (copilot/sonnet-4)
&lt;/h3&gt;&lt;p&gt;OpenCode (configured with the Anthropic &lt;code&gt;sonnet-4&lt;/code&gt; model from Copilot) unsurprisingly produced almost the same code as Claude Code (with the same minor problems), but it also reformatted some existing code without permission. Last but not least, it added two tests (like Claude), but it removed six existing ones 😨&lt;/p&gt;
&lt;p&gt;I had to iterate and tell it to fix, which was very quick to do.&lt;/p&gt;
&lt;h3 id=&#34;opencode-copilotgemini-pro-25&#34;&gt;OpenCode (copilot/gemini-pro-2.5)
&lt;/h3&gt;&lt;p&gt;Gemini… oh dear! I’m not sure if it’s the model itself or if it’s just not optimized for agentic coding (though it should be, since Google just released &lt;code&gt;gemini-cli&lt;/code&gt; 🤷🏻‍♂️). It started hallucinating (I could see this as the code was being written) about existing fixtures, added a new unnecessary one, duplicated some existing code, and completely rewrote an existing class—in a bad way—instead of importing it. I honestly didn’t try a second iteration; I just scrapped the whole thing.&lt;/p&gt;
&lt;h3 id=&#34;opencode-copilotgpt-41&#34;&gt;OpenCode (copilot/gpt-4.1)
&lt;/h3&gt;&lt;p&gt;Last, but not least, I switched to &lt;code&gt;gpt-4.1&lt;/code&gt; from the Copilot subscription. The first iteration wasn’t perfect (it was probably a bit inferior to the one produced by &lt;code&gt;sonnet-4&lt;/code&gt;) and needed a couple of fixes, but once I told it what to correct, it did so quickly, and the final version was perfect.&lt;/p&gt;
&lt;p&gt;Bonus point: using &lt;code&gt;gpt-4.1&lt;/code&gt; through a Copilot subscription offers &lt;strong&gt;unlimited&lt;/strong&gt; usage 🎉&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;The best tool I’ve tried so far remains Claude Code, but I have to admit that OpenCode with &lt;code&gt;sonnet-4&lt;/code&gt; could replace it soon (especially considering it was released less than a month ago). Even &lt;code&gt;gpt-4.1&lt;/code&gt; wasn’t bad, and honestly, using it through OpenCode yields much better results than using it from within VSCode.&lt;/p&gt;
&lt;p&gt;All three models I used with OpenCode tried to reformat existing code. This must be an OpenCode bug, which I’ve tried to mitigate with a rule in the &lt;code&gt;AGENT.md&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;I will keep testing OpenCode over the next few days, and maybe I’ll write a dedicated review about it.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>How to use git worktree effectively with Python projects</title>
        <link>https://www.andreagrandi.it/posts/how-to-use-git-worktree-effectively-with-python-projects/</link>
        <pubDate>Sun, 13 Jul 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/how-to-use-git-worktree-effectively-with-python-projects/</guid>
        <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;Working with multiple branches in Python projects can be challenging, especially when you need to switch between different features, bug fixes, or versions while maintaining separate virtual environments and dependencies.&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://git-scm.com/docs/git-worktree&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;git worktree&lt;/code&gt;&lt;/a&gt; provides an elegant solution to this problem by allowing you to check out multiple branches simultaneously in separate working directories, but it doesn&amp;rsquo;t keep Python development requirements in mind.&lt;/p&gt;
&lt;p&gt;I won&amp;rsquo;t go into details about how to use &lt;code&gt;git worktree&lt;/code&gt; in this post (for that you can check the manual I linked),
but I will explain its limitations and present my workaround.&lt;/p&gt;
&lt;h2 id=&#34;the-problem-with-traditional-branch-switching&#34;&gt;The Problem with Traditional Branch Switching
&lt;/h2&gt;&lt;p&gt;When working on Python projects, switching branches often means recreating the virtual environment,
reinstalling the dependencies, setting the env variables etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git&lt;/code&gt; isn&amp;rsquo;t tight to a specific programming language, so by default it can&amp;rsquo;t do any of this.&lt;/p&gt;
&lt;p&gt;In my case I create the virtual environments with &lt;code&gt;uv&lt;/code&gt; (like I explain in &lt;a class=&#34;link&#34; href=&#34;https://www.andreagrandi.it/posts/using-uv-to-install-python-create-virtualenv/&#34; &gt;this other post&lt;/a&gt;) but this won&amp;rsquo;t be automatically available in the worktree folder and it won&amp;rsquo;t be active.&lt;/p&gt;
&lt;h2 id=&#34;the-helper-script&#34;&gt;The Helper Script
&lt;/h2&gt;&lt;p&gt;To streamline the git worktree workflow with Python projects, I&amp;rsquo;ve created a helper script that automates the common tasks:&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://gist.github.com/andreagrandi/542b438bf0017d93aff2b640037e3ce1&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://gist.github.com/andreagrandi/542b438bf0017d93aff2b640037e3ce1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The script basically does this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it uses &lt;code&gt;git worktree&lt;/code&gt; as usual&lt;/li&gt;
&lt;li&gt;it creates a symbolic link to the existing &lt;code&gt;.venv/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;it creates a symbolic link to the existing &lt;code&gt;.envrc&lt;/code&gt; (which is where I keep my env variables, automatically activated by &lt;code&gt;direnv&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;it runs &lt;code&gt;direnv allow&lt;/code&gt; to give direnv permissions to read the env variables&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You will need to customise the script replacing &lt;code&gt;/my-project-worktrees/&lt;/code&gt; with the name of the folder where you intend to keep your worktrees and &lt;code&gt;/my-project/&lt;/code&gt; with the main folder of your project.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Git worktree is a great tool for working on multiple branches, but it needs some extra setup for Python projects. The helper script makes this much easier by automatically linking your virtual environment and environment variables to new worktrees, saving you time and hassle.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Using terminal-notifier in Claude Code to get custom notifications</title>
        <link>https://www.andreagrandi.it/posts/using-terminal-notifier-claude-code-custom-notifications/</link>
        <pubDate>Sat, 12 Jul 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/using-terminal-notifier-claude-code-custom-notifications/</guid>
        <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;When working with Claude Code on macOS, you might find yourself waiting for long-running tasks to complete or missing important prompts for user input. The &lt;code&gt;terminal-notifier&lt;/code&gt; utility provides an elegant solution by sending desktop notifications directly from the command line, helping you stay informed about your Claude Code sessions even when you&amp;rsquo;re focused on other tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I decided to use &lt;code&gt;terminal-notifier&lt;/code&gt; both because I can customise the notification I receive and also because my setup was having issues and I didn&amp;rsquo;t always get notifications from my terminal.&lt;/p&gt;
&lt;h2 id=&#34;what-is-terminal-notifier&#34;&gt;What is terminal-notifier?
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;terminal-notifier&lt;/code&gt; is a command-line tool for macOS that allows you to send user notifications from the terminal to the Notification Center. It&amp;rsquo;s particularly useful for automation scripts and development workflows where you need to be alerted about specific events.&lt;/p&gt;
&lt;h2 id=&#34;installing-terminal-notifier&#34;&gt;Installing terminal-notifier
&lt;/h2&gt;&lt;p&gt;The easiest way to install &lt;code&gt;terminal-notifier&lt;/code&gt; is through Homebrew:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install terminal-notifier
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;using-terminal-notifier-with-claude-code&#34;&gt;Using terminal-notifier with Claude Code
&lt;/h2&gt;&lt;p&gt;Claude Code can be configured to use &lt;code&gt;terminal-notifier&lt;/code&gt; for sending notifications in different ways. The most realiable (and deterministic)
is using the new &lt;code&gt;/hooks&lt;/code&gt; feature. Add this configuration to your &lt;code&gt;~/.claude/settings.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;Notification&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;matcher&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;terminal-notifier -title \&amp;#34;🔔 Claude Code\&amp;#34; -message \&amp;#34;Claude needs your input\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;Stop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;matcher&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;terminal-notifier -title \&amp;#34;✅ Claude Code\&amp;#34; -message \&amp;#34;The task has been completed\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The other method (not deterministic, but you can customise the message you get)
is to add instructions to your global or project-specific &lt;code&gt;CLAUDE.md&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;## Notification
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; IMPORTANT: YOU MUST ALWAYS DO THIS: When you need to send me a notification because you need input or when you have finished a task, please use terminal-notifier tool like this: terminal-notifier -title &amp;#34;🔔 Claude Code: request&amp;#34; -message &amp;#34;Claude needs your permission to use ...&amp;#34;, or terminal-notifier -title &amp;#34;✅ Claude Code: done&amp;#34; -message &amp;#34;The task has been completed&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; Always customise the message using a short summary of the input needed or the task just completed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Save the file and start a new Claude Code session.&lt;/p&gt;
&lt;h2 id=&#34;final-result&#34;&gt;Final result
&lt;/h2&gt;&lt;p&gt;If everything worked, once Claude has finished elaborating a task, you should get a notification like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/using-terminal-notifier-claude-code-custom-notifications/cc-notification.png&#34;
	width=&#34;750&#34;
	height=&#34;228&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Claude Code notification using terminal notifier&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;328&#34;
		data-flex-basis=&#34;789px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;troubleshooting&#34;&gt;Troubleshooting
&lt;/h2&gt;&lt;h3 id=&#34;notifications-not-appearing&#34;&gt;Notifications Not Appearing
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Check that notifications are enabled for Terminal in System Preferences &amp;gt; Notifications&lt;/li&gt;
&lt;li&gt;Ensure &lt;code&gt;terminal-notifier&lt;/code&gt; is properly installed and in your PATH&lt;/li&gt;
&lt;li&gt;Try running a test notification manually to verify functionality: &lt;code&gt;terminal-notifier -title &amp;quot;Hello&amp;quot; -message &amp;quot;World!&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Integrating &lt;code&gt;terminal-notifier&lt;/code&gt; with Claude Code creates a more efficient and responsive development workflow. By receiving timely notifications about task completion and input requests, you can maintain focus on other work while staying connected to your Claude Code sessions.&lt;/p&gt;
&lt;p&gt;The combination of Claude Code&amp;rsquo;s automation capabilities with macOS&amp;rsquo;s native notification system provides a seamless development experience that keeps you informed without being intrusive.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Using uv to install Python and create a virtual environment</title>
        <link>https://www.andreagrandi.it/posts/using-uv-to-install-python-create-virtualenv/</link>
        <pubDate>Thu, 30 Jan 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/using-uv-to-install-python-create-virtualenv/</guid>
        <description>&lt;p&gt;Following my &lt;a class=&#34;link&#34; href=&#34;https://www.andreagrandi.it/posts/stepping-my-feet-into-uv-world-part-1/&#34; &gt;previous post&lt;/a&gt;, today I wanted to see if I could replace my &lt;code&gt;pyenv&lt;/code&gt; and &lt;code&gt;pyenv-virtualenv&lt;/code&gt; &lt;a class=&#34;link&#34; href=&#34;https://www.andreagrandi.it/posts/install-python-with-pyenv-and-pyenvvirtualenv-create-virtual-environment-with-specific-python-version-macos/&#34; &gt;usage&lt;/a&gt; with &lt;code&gt;uv&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;install-uv&#34;&gt;Install uv
&lt;/h2&gt;&lt;p&gt;If you haven&amp;rsquo;t done it yet, you need to first install &lt;code&gt;uv&lt;/code&gt; using either this method or one of the methods &lt;a class=&#34;link&#34; href=&#34;https://docs.astral.sh/uv/getting-started/installation/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;described in the documentation&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install uv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;install-python&#34;&gt;Install Python
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; can detect Python versions installed in different ways in the system or it can install its own copies. You can check which ones are installed, using this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uv python list --only-installed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now we are going to install the latest (at the time of writing) version of &lt;strong&gt;Python&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uv python install 3.13.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;create-a-virtual-environment&#34;&gt;Create a virtual environment
&lt;/h2&gt;&lt;p&gt;Creating virtual environment with &lt;code&gt;uv&lt;/code&gt; is quite easy. We can do it in this way:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uv venv --python 3.13.1 --prompt my-project
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;With &lt;code&gt;--python 3.13.1&lt;/code&gt; we specify the Python version we want and with &lt;code&gt;--prompt my-project&lt;/code&gt; we customise the text that will appear in the prompt.&lt;/p&gt;
&lt;p&gt;As the output will say, we can simply activate the environment with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;automatically-activate-the-virtual-environment&#34;&gt;Automatically activate the virtual environment
&lt;/h3&gt;&lt;p&gt;Running &lt;code&gt;source .venv/bin/activate&lt;/code&gt; every time we enter the project directory can be boring. We can automate this by using &lt;a class=&#34;link&#34; href=&#34;https://direnv.net&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;direnv&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;direnv&lt;/code&gt; is a tool which can automatically set environment variables or run simple commands when we enter inside a directory.&lt;/p&gt;
&lt;p&gt;You can install it with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install direnv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Once it&amp;rsquo;s installed, we need to add this configuration inside our &lt;code&gt;.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;eval&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;direnv hook zsh&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;or, in case we are using &lt;code&gt;bash&lt;/code&gt;, we need to do it inside &lt;code&gt;.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;eval&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;direnv hook bash&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;At this point simply close and reopen your terminal (it&amp;rsquo;s the quickest way to reload the configuration) and finally create a file named &lt;code&gt;.envrc&lt;/code&gt; inside your project folder, containing these two lines (or append these two lines in case you are already using this file):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;VIRTUAL_ENV&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PWD&lt;/span&gt;/.venv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VIRTUAL_ENV&lt;/span&gt;/bin:&lt;span class=&#34;nv&#34;&gt;$PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;After you save this file and you get back to your terminal, you will get an error like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;direnv: error /Users/andrea/Projects/my-project/.envrc is blocked. Run &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;direnv allow&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; to approve its content
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Just run that command to allow direnv to load your configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;direnv allow
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;installing-python-packages&#34;&gt;Installing Python packages
&lt;/h2&gt;&lt;p&gt;At this point you can use &lt;code&gt;uv pip&lt;/code&gt; (which is a drop in replacement for &lt;code&gt;pip&lt;/code&gt; with a few &lt;a class=&#34;link&#34; href=&#34;https://docs.astral.sh/uv/pip/compatibility/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;limitations&lt;/a&gt;) just like you would use &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uv pip install requests
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;make-an-alias-for-uv-pip&#34;&gt;Make an alias for uv pip
&lt;/h2&gt;&lt;p&gt;If you are happy to use the &lt;code&gt;uv&lt;/code&gt; version of &lt;code&gt;pip&lt;/code&gt;, you can use it anywhere, even in virtual environments which are not managed by &lt;code&gt;uv&lt;/code&gt;. You can create an alias by adding this to your &lt;code&gt;.zshrc&lt;/code&gt; (or &lt;code&gt;.bashrc&lt;/code&gt; etc&amp;hellip;)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;alias&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;pip&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;uv pip&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This way, every time you invoke &lt;code&gt;pip&lt;/code&gt;, you will use the &lt;code&gt;uv&lt;/code&gt; version:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pip --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Using &lt;code&gt;uv pip&lt;/code&gt;, &lt;code&gt;uv venv&lt;/code&gt; and &lt;code&gt;uv python&lt;/code&gt; you can definitely speed up Python installation, virtual environments creations and Python packages installation.&lt;/p&gt;
&lt;p&gt;But &lt;code&gt;uv&lt;/code&gt; is much more. You can also manage dependencies, &lt;a class=&#34;link&#34; href=&#34;https://docs.astral.sh/uv/guides/projects/#uvlock&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;lock them&lt;/strong&gt;&lt;/a&gt; and do many other things. At this point I don&amp;rsquo;t feel like switching completely to this tool, especially because a few things (like dependencies locking) are not standard across Python ecosystem and the risk is to use a workflow which is not supported by other existing tools (for example if you are using &lt;strong&gt;dependabot&lt;/strong&gt; you won&amp;rsquo;t be able to just use &lt;code&gt;uv.lock&lt;/code&gt; file, you will need to &lt;a class=&#34;link&#34; href=&#34;https://docs.astral.sh/uv/pip/compile/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;extract&lt;/a&gt; &lt;code&gt;requirements.txt&lt;/code&gt; files)&lt;/p&gt;
&lt;p&gt;My final advice is to use just the parts which are compatible with your existing workflow and wait until a standard for locking dependencies will be universally accepted, before jumping in with both feet.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Stepping my feet into uv world - part 1</title>
        <link>https://www.andreagrandi.it/posts/stepping-my-feet-into-uv-world-part-1/</link>
        <pubDate>Wed, 29 Jan 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/stepping-my-feet-into-uv-world-part-1/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/stepping-my-feet-into-uv-world-part-1/uv-speed.png" alt="Featured image of post Stepping my feet into uv world - part 1" /&gt;&lt;p&gt;I heard about &lt;a class=&#34;link&#34; href=&#34;https://docs.astral.sh/uv/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;uv&lt;/code&gt;&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://astral.sh&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Astral&lt;/strong&gt;&lt;/a&gt; last year and as I previously mentioned in &lt;a class=&#34;link&#34; href=&#34;https://www.andreagrandi.it/posts/building-healthy-sustainable-funding-model-opensource-software/&#34; &gt;this blog post&lt;/a&gt;, I did (and still have) have some concerns about them.&lt;/p&gt;
&lt;p&gt;By the way, inspired by some work a colleague of mine is doing, I wanted to give it a chance and I checked if I could start using it without disrupting my &lt;code&gt;pyenv&lt;/code&gt; &lt;a class=&#34;link&#34; href=&#34;https://www.andreagrandi.it/posts/install-python-with-pyenv-and-pyenvvirtualenv-create-virtual-environment-with-specific-python-version-macos/&#34; &gt;workflow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Even if you don&amp;rsquo;t want to migrate away from your favourite Python environment and virtualenv manager, there are a few ways to use it to speed up your existing configuration, so stay with me.&lt;/p&gt;
&lt;h2 id=&#34;what-is-uv&#34;&gt;What is uv?
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; is a package manager for &lt;strong&gt;Python&lt;/strong&gt; which is written in &lt;strong&gt;Rust&lt;/strong&gt;, it&amp;rsquo;s extremely fast and it&amp;rsquo;s becoming the favourite package manager across the Python community. As usual, please refer to the official website for more details: &lt;a class=&#34;link&#34; href=&#34;https://docs.astral.sh/uv/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://docs.astral.sh/uv/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;installing-uv&#34;&gt;Installing uv
&lt;/h2&gt;&lt;p&gt;It seems obvious but before proceeding with this tutorial, you need to install &lt;code&gt;uv&lt;/code&gt;. There are a few different ways described in the official &lt;a class=&#34;link&#34; href=&#34;https://docs.astral.sh/uv/getting-started/installation/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;documentation&lt;/a&gt;, but if you are using &lt;strong&gt;MacOS&lt;/strong&gt; and also use &lt;code&gt;brew&lt;/code&gt;, you can install it with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install uv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;using-uv-pip&#34;&gt;Using uv pip
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; reimplements &lt;code&gt;pip&lt;/code&gt; so instead of doing &lt;code&gt;pip install -r requirements.txt&lt;/code&gt; you can do &lt;code&gt;uv pip install requirements.txt&lt;/code&gt; (or similar), and believe me: It&amp;rsquo;s faster!&lt;/p&gt;
&lt;p&gt;This is a &lt;code&gt;pip&lt;/code&gt; command I used to upgrade the dependencies in the virtual environment of one of the work projects (I only renamed the project name)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;❯&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uv&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pip&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;U&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;requirements&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;txt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Using&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Python&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;3.12.8&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;environment&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Users&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;andrea&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pyenv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;versions&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;3.12.8&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;envs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;project&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Resolved&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;74&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;packages&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;879&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Prepared&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;packages&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1.50&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Uninstalled&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;packages&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;300&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Installed&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;packages&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;alembic&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.14.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;alembic&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.14.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;24.3.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attrs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;25.1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;boto3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.35.98&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;boto3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.36.2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;botocore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.35.99&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;botocore&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.36.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;deprecated&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.2.15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;deprecated&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.2.18&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s3transfer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.10.4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s3transfer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.11.2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sentry&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sdk&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;2.19.2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sentry&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sdk&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;2.20.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;structlog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;24.4.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;structlog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;25.1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The speed is impressive! I almost thought the command had failed because it finished so quickly. You can find more details in the docs: &lt;a class=&#34;link&#34; href=&#34;https://docs.astral.sh/uv/pip/packages/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://docs.astral.sh/uv/pip/packages/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;using-noxuv&#34;&gt;Using nox[uv]
&lt;/h2&gt;&lt;p&gt;For the same project, we use &lt;a class=&#34;link&#34; href=&#34;https://nox.thea.codes/en/stable/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;nox&lt;/code&gt;&lt;/a&gt; as a wrapper to run tests and it takes care of creating specific virtual environments. By default it uses &lt;code&gt;virtualenv&lt;/code&gt; but there is a simple trick to make it use &lt;code&gt;uv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First you need to install &lt;code&gt;nox[uv]&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uv tool install &lt;span class=&#34;s1&#34;&gt;&amp;#39;nox[uv]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Then you just need to set this environment variable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;NOX_DEFAULT_VENV_BACKEND&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;uv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it! I can&amp;rsquo;t provide the full output of our test suite, but I timed (&lt;code&gt;time nox -s test ...&lt;/code&gt;) the execution and by using &lt;code&gt;uv&lt;/code&gt; as backend, our virtual environment is created &lt;strong&gt;13 seconds faster&lt;/strong&gt;!&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;I used &amp;ldquo;part 1&amp;rdquo; as suffix for this blog post, because I&amp;rsquo;m sure these experiments will continue in the next days, by using more of the available &lt;code&gt;uv&lt;/code&gt; tools. Once I&amp;rsquo;ve learned more, I will share my experience again.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>How to workaround Ollama models pull issues</title>
        <link>https://www.andreagrandi.it/posts/how-to-workaround-ollama-pull-issues/</link>
        <pubDate>Sun, 26 Jan 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/how-to-workaround-ollama-pull-issues/</guid>
        <description>&lt;p&gt;In the past couple of days I was having this annoying &lt;strong&gt;issue&lt;/strong&gt; while trying to pull a model from &lt;strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://ollama.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Ollama&lt;/a&gt;&lt;/strong&gt;: I was running the command &lt;code&gt;ollama pull deepseek-r1:8b&lt;/code&gt;, it was downloading something like 4-5% of the model and then &lt;strong&gt;the connection was reset&lt;/strong&gt;, the client was &amp;ldquo;crashing&amp;rdquo; and &lt;strong&gt;it always restarted from 0%&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It looks like I wasn&amp;rsquo;t alone: &lt;a class=&#34;link&#34; href=&#34;https://github.com/ollama/ollama/issues/8406&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/ollama/ollama/issues/8406&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I&amp;rsquo;m running Ollama &lt;code&gt;0.5.7&lt;/code&gt;, it&amp;rsquo;s likely they will fix the issue in one of the next releases.&lt;/p&gt;
&lt;h2 id=&#34;solution&#34;&gt;Solution
&lt;/h2&gt;&lt;p&gt;Someone kindly posted a &lt;a class=&#34;link&#34; href=&#34;https://github.com/ollama/ollama/issues/8535#issuecomment-2613241807&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;workaround&lt;/a&gt; which is a bash script able to invoke Ollama client and resume the download where it was left (Ollama client should to that, but it&amp;rsquo;s not doing it correctly when it crashes).&lt;/p&gt;
&lt;p&gt;Save this script in a file like &lt;code&gt;ollama-pull.sh&lt;/code&gt; and make it executable with &lt;code&gt;chmod +x ollama-pull.sh&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;die&lt;span class=&#34;o&#34;&gt;(){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#DRYRUN=echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt; -v jq&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;Need jq&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt; -v curl&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;Need curl&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -z &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;usage: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$0&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; modelname&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;%%:*&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; *:* &lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;tag&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;##*:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;tag&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;latest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;OLLAMA_MODELS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;OLLAMA_MODELS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;-/usr/share/ollama/.ollama/models&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$OLLAMA_MODELS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;Couldn&amp;#39;t cd to OLLAMA_MODELS (&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$OLLAMA_MODELS&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; ! -d blobs -o ! -d manifests &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;Missing blobs or manifests directory&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;manifest_dir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;manifests/registry.ollama.ai/library/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -e &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest_dir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; already exists&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; ! -d &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest_dir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$DRYRUN&lt;/span&gt; mkdir -p &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest_dir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;Couldn&amp;#39;t mkdir manifest dir (&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest_dir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;)&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;manifest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;curl -sL https://registry.ollama.ai/v2/library/&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;/manifests/&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;Couldn&amp;#39;t fetch manifest&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;errors&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;jq -cn &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; |.errors&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$errors&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;null&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$errors&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;jq -rn &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; | .config.digest&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;No config digest&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$DRYRUN&lt;/span&gt; curl -#L -C - -o blobs/&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;/:/-&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt; https://registry.ollama.ai/v2/library/&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;/blobs/&lt;span class=&#34;nv&#34;&gt;$config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;Couldn&amp;#39;t fetch config blob&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; layer in &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;jq -rn &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; | .layers[].digest&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nv&#34;&gt;$DRYRUN&lt;/span&gt; curl -#L -C - -o blobs/&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;layer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;/:/-&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt; https://registry.ollama.ai/v2/library/&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;/blobs/&lt;span class=&#34;nv&#34;&gt;$layer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;Couldn&amp;#39;t fetch layer&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$DRYRUN&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;echo &amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39; &amp;gt; &amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest_dir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$manifest_dir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; die &lt;span class=&#34;s2&#34;&gt;&amp;#34;Couldn&amp;#39;t write manifest&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;and run the script in this way:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;OLLAMA_MODELS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/.ollama/models ./ollama-pull.sh deepseek-r1:8b
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;(replace &lt;code&gt;deepseek-r1:8b&lt;/code&gt; with the &lt;code&gt;name:tag&lt;/code&gt; of the model you want to pull)&lt;/p&gt;
</description>
        </item>
        <item>
        <title>I got a Micro.one blog</title>
        <link>https://www.andreagrandi.it/posts/i-got-micro-one-blog/</link>
        <pubDate>Tue, 21 Jan 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/i-got-micro-one-blog/</guid>
        <description>&lt;p&gt;I found out about &lt;strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://micro.blog&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Micro.blog&lt;/a&gt;&lt;/strong&gt; platform thanks to &lt;strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://mastodon.social/@webology&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Jeff Triplett&lt;/a&gt;&lt;/strong&gt; a few
months ago and I even decided to give it a try, but to be honest, having already a main blog, made with &lt;a class=&#34;link&#34; href=&#34;https://gohugo.io&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hugo&lt;/a&gt;, which is hosted for free on &lt;a class=&#34;link&#34; href=&#34;https://pages.github.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GitHub Pages&lt;/a&gt;, I couldn&amp;rsquo;t really justify spending another &lt;strong&gt;$5/month&lt;/strong&gt; for a micro blogging platform.&lt;/p&gt;
&lt;h2 id=&#34;microone&#34;&gt;Micro.one
&lt;/h2&gt;&lt;p&gt;Then &lt;a class=&#34;link&#34; href=&#34;https://www.manton.org/about/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Manton Reece&lt;/a&gt; (the creator of Micro.blog) announced (actually he didn&amp;rsquo;t do it officially when I discovered it, but I found out anyway!) a new plan: &lt;strong&gt;Micro.one&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/i-got-micro-one-blog/micro-one-plan.png&#34;
	width=&#34;1003&#34;
	height=&#34;404&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Micro.one, Micro.blog and Micro.blog premium plans&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;248&#34;
		data-flex-basis=&#34;595px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;just $1/month&lt;/strong&gt; (or $10/year if you switch to the annual plan!) I can have my micro blog hosted, with custom domain support, I can post text, photos and even podcasts!&lt;/p&gt;
&lt;h2 id=&#34;what-is-microblog&#34;&gt;What is Micro.blog?
&lt;/h2&gt;&lt;p&gt;Micro.blog (and Micro.one too) is an hosting solution for static blogs. The user selects a template, creates the content and the platform uses Hugo to create the pages which are then published.&lt;/p&gt;
&lt;p&gt;Nothing revolutionary for me, but I reckon this is a &lt;strong&gt;quite good solution&lt;/strong&gt; for many people who would like to have a blog but they don&amp;rsquo;t have the skills to use Hugo or create a CI jobs which publish to GitHub Pages.&lt;/p&gt;
&lt;h2 id=&#34;why-a-micro-blog&#34;&gt;Why a micro blog?
&lt;/h2&gt;&lt;p&gt;I often have content to post which is too short to justify a proper blog post but it&amp;rsquo;s too long to be posted on &lt;strong&gt;Mastodon&lt;/strong&gt; or &lt;strong&gt;BlueSky&lt;/strong&gt;. For this type of posts, Micro.blog is perfect!&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;If you are looking for an easy to use solution to create a personal blog, look no further. Oh, and you can &lt;strong&gt;visit mine&lt;/strong&gt; at this address: &lt;a class=&#34;link&#34; href=&#34;https://micro.andreagrandi.it&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://micro.andreagrandi.it&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>New Year resolution: sponsoring some of the open source projects I use</title>
        <link>https://www.andreagrandi.it/posts/new-year-resolution-sponsoring-opensource-projects/</link>
        <pubDate>Fri, 03 Jan 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/new-year-resolution-sponsoring-opensource-projects/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/new-year-resolution-sponsoring-opensource-projects/sponsor.png" alt="Featured image of post New Year resolution: sponsoring some of the open source projects I use" /&gt;&lt;p&gt;After having thought about this for a while, I finally decided to start sponsoring some of the open source projects I regularly use.
I set aside a monthly budget and I evenly divided the amount across the projects.&lt;/p&gt;
&lt;h2 id=&#34;who-am-i-sponsoring&#34;&gt;Who am I sponsoring?
&lt;/h2&gt;&lt;p&gt;So far I&amp;rsquo;ve started sponsoring these projects / people (in no particular order):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/gnachman/iTerm2&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;iTerm2&lt;/a&gt; - a terminal for MacOS I&amp;rsquo;ve been using for years&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/tiangolo&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Sebastián Ramírez&lt;/a&gt; - author of &lt;a class=&#34;link&#34; href=&#34;https://fastapi.tiangolo.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;FastAPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Homebrew&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Homebrew&lt;/a&gt; - a package manager for MacOS&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/cryptomator&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Cryptomator&lt;/a&gt; - a tool to encrypt, decrypt and easily access files in various clouds&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/pallets&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Pallets&lt;/a&gt; - they develop and maintain Flask, Click and a few other tools&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/mastodon&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Mastodon&lt;/a&gt; - the social network I use most&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/atuinsh&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Atuin&lt;/a&gt; - a shell history which supports a few different shells&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-much-am-i-donating&#34;&gt;How much am I donating?
&lt;/h2&gt;&lt;p&gt;If you were expecting to find an amount, please move to the next paragraph 😅&lt;/p&gt;
&lt;p&gt;What I can say is that everyone should donate an amount based on their own possibilities and estabilished budget. &lt;strong&gt;If all the users&lt;/strong&gt; of the projects I mentioned, &lt;strong&gt;donated&lt;/strong&gt; as much as I&amp;rsquo;m doing, &lt;strong&gt;those developers would be rich&lt;/strong&gt; 😉&lt;/p&gt;
&lt;h2 id=&#34;how-am-i-donating&#34;&gt;How am I donating?
&lt;/h2&gt;&lt;p&gt;For my own simplicity, I decided to support only projects with a &lt;strong&gt;Sponsor page on GitHub&lt;/strong&gt;. This way I can manage all the donations from a single website. I can decide to adjust the amounts and to sponsor more projects. I don&amp;rsquo;t want to discriminate anyone, but at the end of the day it&amp;rsquo;s up to me deciding who to support and how 🤷🏻‍♂️&lt;/p&gt;
&lt;h2 id=&#34;could-i-do-more&#34;&gt;Could I do more?
&lt;/h2&gt;&lt;p&gt;Yes, but&amp;hellip; there was a specific thing which prevented me from donating to other projects I wanted to sponsor. These projects (which I won&amp;rsquo;t mention, but I&amp;rsquo;ve contacted them in private to suggest considering a more flexible option) all &lt;strong&gt;have a minimum amount set&lt;/strong&gt; in their Sponsor settings.&lt;/p&gt;
&lt;p&gt;Honestly I can&amp;rsquo;t understand the reason for this, and I&amp;rsquo;m sure it&amp;rsquo;s not about fees, because &lt;a class=&#34;link&#34; href=&#34;https://docs.github.com/en/sponsors/sponsoring-open-source-contributors/about-sponsorships-fees-and-taxes#sponsorship-fees&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GitHub&lt;/a&gt; takes a fixed fees percentage (3% credit card processing fee + 3% GitHub service processing fee) so receiving 1$ from 100 people or 100$ from 1 person doesn&amp;rsquo;t change the final amount of money the developer receives.&lt;/p&gt;
&lt;p&gt;My humble suggestion is to &lt;strong&gt;remove the minimum amount&lt;/strong&gt;: not everyone can or is willing to donate the minimum amount some of these projects have set and you will be able to collect more donations from more people.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;My personal opinion is that every cent counts. No matter if you can only afford 5$/month or if you are able to donate 100$/month: &lt;strong&gt;every person should donate based on their own preferences and possibilities.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I onestly would prefer to get 1$ from 100 different people than 100$ from a single person. A larger number of people believing in my projects is more important than the final amount.&lt;/p&gt;
&lt;p&gt;So, if you really care about open source, beware that GitHub stars can&amp;rsquo;t be used to pay the bills 😉 &lt;strong&gt;please consider donating&lt;/strong&gt; to the projects you love as much as you can!&lt;/p&gt;
</description>
        </item>
        <item>
        <title>My ZSH history</title>
        <link>https://www.andreagrandi.it/posts/my-zsh-history/</link>
        <pubDate>Thu, 02 Jan 2025 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/my-zsh-history/</guid>
        <description>&lt;p&gt;Inspired by &lt;a class=&#34;link&#34; href=&#34;https://nicolaiarocci.com/my-most-used-command-line-commands/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;this post&lt;/a&gt; from &lt;strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://nicolaiarocci.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Nicola Iarocci&lt;/a&gt;&lt;/strong&gt;, I decided to check my own &lt;del&gt;bash&lt;/del&gt; &lt;strong&gt;zsh&lt;/strong&gt; history:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1082 git&lt;/li&gt;
&lt;li&gt;99 cd&lt;/li&gt;
&lt;li&gt;95 nox&lt;/li&gt;
&lt;li&gt;38 pip&lt;/li&gt;
&lt;li&gt;37 ls&lt;/li&gt;
&lt;li&gt;33 pytest&lt;/li&gt;
&lt;li&gt;29 brew&lt;/li&gt;
&lt;li&gt;26 code&lt;/li&gt;
&lt;li&gt;19 curl&lt;/li&gt;
&lt;li&gt;16 pyenv&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even for me, my biggest usage of the terminal is because of &lt;strong&gt;git&lt;/strong&gt; 😅&lt;/p&gt;
&lt;p&gt;You can produce your own list by using this command: &lt;code&gt;history | awk &#39;{print $2}&#39; | sort | uniq --count | sort --numeric-sort --reverse | head -10&lt;/code&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>I got a Basset Hound dog: welcome Fulvio!</title>
        <link>https://www.andreagrandi.it/posts/welcome-fulvio/</link>
        <pubDate>Sat, 14 Dec 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/welcome-fulvio/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/welcome-fulvio/fulvio_cover.jpg" alt="Featured image of post I got a Basset Hound dog: welcome Fulvio!" /&gt;&lt;p&gt;I&amp;rsquo;ve always wanted a dog (actually&amp;hellip; my family had one ~30 years ago, but since I wasn&amp;rsquo;t directly involved with him, I never felt him as my own). Life (and sometimes laziness) had not allowed me to own one in the past (living abroad, moving back to
Italy but living in a small apartment etc&amp;hellip;) so when I finally had the chance, I got him!&lt;/p&gt;
&lt;p&gt;His name is &lt;strong&gt;Fulvio&lt;/strong&gt; and he&amp;rsquo;s a &lt;strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Basset_Hound&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Basset Hound&lt;/a&gt;&lt;/strong&gt; breed (yes, the same breed as &lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Columbo_%28character%29&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Lieutenant Columbo&lt;/strong&gt;&lt;/a&gt; one 😅)&lt;/p&gt;
&lt;p&gt;Since I got him, &lt;strong&gt;my life has definitely changed&lt;/strong&gt;. Mostly for &lt;strong&gt;good things&lt;/strong&gt; (like doing a &lt;strong&gt;45-50 minutes walk every morning&lt;/strong&gt; which is helping me a lot to be more healthy!) but there are definitely things I still need to get used to 🥲&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m lucky to have 3 weeks off for this Christmas holidays and this means plenty of time to play with him and to get to know each other.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t have much else to say, so I will leave you with another picture of him.&lt;/p&gt;
&lt;p&gt;Greeting from me and Fulvio! 👋 🐶&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/welcome-fulvio/fulvio_sleeping.jpg&#34;
	width=&#34;1200&#34;
	height=&#34;1600&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;A picture of Fulvio, the basset hound, sleeping&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;75&#34;
		data-flex-basis=&#34;180px&#34;
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>How to configure an Ethernet connection between iPad and RaspberryPi through USB-C</title>
        <link>https://www.andreagrandi.it/posts/howto-configure-ethernet-connection-ipad-raspberrypi-usbc/</link>
        <pubDate>Sat, 26 Oct 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/howto-configure-ethernet-connection-ipad-raspberrypi-usbc/</guid>
        <description>&lt;p&gt;I recently found out (thanks to &lt;a class=&#34;link&#34; href=&#34;https://hamatti.org/posts/my-on-the-go-solution-with-ipad-and-raspberry-pi/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;this article&lt;/a&gt; from &lt;strong&gt;Juha-Matti Santala&lt;/strong&gt;) that not only &lt;strong&gt;you can power a RaspberryPi device through the USB-C&lt;/strong&gt; of an iPad but that you can also &lt;strong&gt;get an ethernet connection&lt;/strong&gt; through it&lt;/p&gt;
&lt;h2 id=&#34;requirements&#34;&gt;Requirements
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;an &lt;strong&gt;USB-C iPad&lt;/strong&gt; (I used an iPad Air 4th gen)&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;USB-C cable&lt;/strong&gt; (I used the one that comes with the iPad)&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;RaspberryPi&lt;/strong&gt; (you need at least a RPI 4. I used a RPi 5)&lt;/li&gt;
&lt;li&gt;you already have &lt;strong&gt;SSH access to the RaspberryPi&lt;/strong&gt; (I won&amp;rsquo;t cover this part in this tutorial)&lt;/li&gt;
&lt;li&gt;an updated bootloader on the RaspberryPi (check &lt;a class=&#34;link&#34; href=&#34;https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#update-the-bootloader&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;this documentation&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;configuration&#34;&gt;Configuration
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Connect to the RaspberryPi using SSH&lt;/li&gt;
&lt;li&gt;Become &lt;strong&gt;super user&lt;/strong&gt; on the RaspberryPi: &lt;code&gt;sudo su&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Edit &lt;code&gt;/boot/firmware/cmdline.txt&lt;/code&gt; and add these options &lt;strong&gt;after &lt;code&gt;rootwait&lt;/code&gt;&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;modules-load=dwc2,g_ether
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Edit &lt;code&gt;/boot/firmware/config.txt&lt;/code&gt; and make sure that &lt;code&gt;otg_mode=1&lt;/code&gt; is &lt;strong&gt;uncommented&lt;/strong&gt;.
Then add this line after the &lt;code&gt;[all]&lt;/code&gt; section:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dtoverlay=dwc2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;Use this command to add a new connection:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nmcli con add type ethernet con-name ethernet-usb0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;6&#34;&gt;
&lt;li&gt;Edit &lt;code&gt;/etc/NetworkManager/system-connections/ethernet-usb0.nmconnection&lt;/code&gt; and make sure you have these settings:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[connection]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;id=ethernet-usb0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uuid=&amp;lt;random group of characters here&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;type=ethernet
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;autoconnect=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;interface-name=usb0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[ethernet]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[ipv4]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;method=shared
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[ipv6]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;addr-gen-mode=default
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;method=auto
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[proxy]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;7&#34;&gt;
&lt;li&gt;Edit &lt;code&gt;/usr/local/sbin/usb-gadget.sh&lt;/code&gt; and add this:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nmcli con up ethernet-usb0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;8&#34;&gt;
&lt;li&gt;Make the file executable:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod a+rx /usr/local/sbin/usb-gadget.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;9&#34;&gt;
&lt;li&gt;Edit &lt;code&gt;/lib/systemd/system/usbgadget.service&lt;/code&gt; and add this:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[Unit]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Description=My USB gadget
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;After=NetworkManager.service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Wants=NetworkManager.service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[Service]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Type=oneshot
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;RemainAfterExit=yes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ExecStart=/usr/local/sbin/usb-gadget.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[Install]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;WantedBy=sysinit.target
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;10&#34;&gt;
&lt;li&gt;Enable the service using this command:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl enable usbgadget.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;11&#34;&gt;
&lt;li&gt;Reboot the RaspberryPi:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;reboot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;check-the-configuration-is-working&#34;&gt;Check the configuration is working
&lt;/h2&gt;&lt;p&gt;Connect the RaspberryPi to the iPad using the USB-C cable and wait for it to boot. Once it finishes booting up, open &lt;strong&gt;Settings&lt;/strong&gt;
on the iPad and check if there is an &lt;strong&gt;Ethernet&lt;/strong&gt; section below the &lt;strong&gt;WiFi Connection&lt;/strong&gt;. If you find the entry, then it means the connection is successful.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&lt;/h2&gt;&lt;p&gt;This tutorial has been written thanks to this other tutorial &lt;a class=&#34;link&#34; href=&#34;https://github.com/verxion/RaspberryPi/blob/main/Pi5-ethernet-and-power-over-usbc.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/verxion/RaspberryPi/blob/main/Pi5-ethernet-and-power-over-usbc.md&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>My User Manual</title>
        <link>https://www.andreagrandi.it/posts/my-user-manual/</link>
        <pubDate>Wed, 11 Sep 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/my-user-manual/</guid>
        <description>&lt;p&gt;This is my &lt;a class=&#34;link&#34; href=&#34;https://futureforum.com/2022/07/15/personal-user-manual/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;user manual&lt;/strong&gt;&lt;/a&gt;. If we are working together (or planning to), please read it carefully. You will &lt;strong&gt;learn how to deal with me productively&lt;/strong&gt; and avoid misunderstandings. I initially wrote this for my current colleagues, but I thought it could be a good idea to make it public for everyone else I daily interact with.&lt;/p&gt;
&lt;h2 id=&#34;my-style&#34;&gt;My style
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;I need to understand &lt;strong&gt;why&lt;/strong&gt; I&amp;rsquo;m doing something&lt;/li&gt;
&lt;li&gt;I love working as a part of a team, not as an individual&lt;/li&gt;
&lt;li&gt;I love pair programming&lt;/li&gt;
&lt;li&gt;I like to have a very basic working version first and iterate on it&lt;/li&gt;
&lt;li&gt;I love to help&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;what-i-value&#34;&gt;What I value
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;honesty&lt;/li&gt;
&lt;li&gt;direct and straight communication&lt;/li&gt;
&lt;li&gt;people who quickly get to the point&lt;/li&gt;
&lt;li&gt;leading by example&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;what-i-dont-have-patience-for&#34;&gt;What I don’t have patience for
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;process for the sake of process&lt;/li&gt;
&lt;li&gt;long meetings where no actions are taken at the end&lt;/li&gt;
&lt;li&gt;indirect feedback (heard from third parties)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-best-to-communicate-with-me&#34;&gt;How best to communicate with me
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;chat (public channel unless it&amp;rsquo;s personal)&lt;/li&gt;
&lt;li&gt;email is ok&amp;hellip;ish&lt;/li&gt;
&lt;li&gt;absolutely not by phone&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-i-make-decisions&#34;&gt;How I make decisions
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;I need some context first&lt;/li&gt;
&lt;li&gt;I understand the problem&lt;/li&gt;
&lt;li&gt;I draft some options and evaluate them&lt;/li&gt;
&lt;li&gt;I pick the one which is less complicated but at the same time doesn&amp;rsquo;t prevent further improvements&lt;/li&gt;
&lt;li&gt;I iterate and improve the implemented solution&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;how-to-help-me&#34;&gt;How to help me
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;give me &lt;strong&gt;cooking tips not ready meals!&lt;/strong&gt; - I appreciate help and I don&amp;rsquo;t hesitate asking for it when I need, but whenever I do I like to be given tips to unblock me not a ready solution or someone else doing the task for me.&lt;/li&gt;
&lt;li&gt;offer me to do &lt;strong&gt;pair programming&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;random-things-i-like&#34;&gt;Random things I like
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;dogs (especially Basset Hounds)&lt;/li&gt;
&lt;li&gt;music (listening to and playing acoustic guitar, bass guitar and analogic synths)&lt;/li&gt;
&lt;li&gt;cooking&lt;/li&gt;
&lt;li&gt;photography&lt;/li&gt;
&lt;li&gt;learning new cool things&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;random-things-i-dislike&#34;&gt;Random things I dislike
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;warm beer&lt;/li&gt;
&lt;li&gt;Italian food blasphemy (like putting pineapple on pizza, &amp;ldquo;fettucine Alfredo&amp;rdquo;, etc&amp;hellip; 😜 )&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Building a healthy and sustainable funding model for open source software</title>
        <link>https://www.andreagrandi.it/posts/building-healthy-sustainable-funding-model-opensource-software/</link>
        <pubDate>Sat, 07 Sep 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/building-healthy-sustainable-funding-model-opensource-software/</guid>
        <description>&lt;p&gt;If you work in software development, chances are high that your company is &lt;strong&gt;using open source software to build their products&lt;/strong&gt;. Nowadays open source software is nearly everywhere: it can be included in your operating system, in commercial software or it can be responsible for hosting your website or your application.&lt;/p&gt;
&lt;p&gt;In this post I will focus mainly on &lt;strong&gt;languages, libraries and frameworks&lt;/strong&gt; which are being used to build software applications, because they are the components I&amp;rsquo;m most familiar with.&lt;/p&gt;
&lt;h2 id=&#34;context&#34;&gt;Context
&lt;/h2&gt;&lt;p&gt;Suppose your company needs to build a new software service which is going to be offered as an API. You will need to pick a &lt;strong&gt;language&lt;/strong&gt; (for example &lt;strong&gt;Python&lt;/strong&gt;), possibly &lt;strong&gt;a web framework&lt;/strong&gt; (like &lt;strong&gt;Django&lt;/strong&gt;, &lt;strong&gt;Flask&lt;/strong&gt; or &lt;strong&gt;FastAPI&lt;/strong&gt;) and some additional &lt;strong&gt;libraries&lt;/strong&gt; (like &lt;strong&gt;SQLAlchemy&lt;/strong&gt;, &lt;strong&gt;requests&lt;/strong&gt;, etc&amp;hellip;) to build your service.&lt;/p&gt;
&lt;p&gt;Usually developers will pick the tools they are most familiar with, or the tools which are most popular in the community, but very few people ask themselves: &lt;strong&gt;who is behind these tools? Who is maintaining them? Who is fixing the bugs? Who is adding new features?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And most importantly: &lt;strong&gt;how are these people being paid?&lt;/strong&gt; (You are not expecting them to work for free, right? 😏)&lt;/p&gt;
&lt;h2 id=&#34;the-current-funding-models&#34;&gt;The current funding models
&lt;/h2&gt;&lt;p&gt;Some languages and frameworks have a no profit foundation behind them (ie: &lt;a class=&#34;link&#34; href=&#34;https://www.python.org&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/a&gt; has &lt;a class=&#34;link&#34; href=&#34;https://www.python.org/psf-landing/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Python Software Foundation&lt;/strong&gt;&lt;/a&gt;, &lt;a class=&#34;link&#34; href=&#34;https://www.djangoproject.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Django&lt;/strong&gt;&lt;/a&gt; has &lt;a class=&#34;link&#34; href=&#34;https://www.djangoproject.com/foundation/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Django Software Foundation&lt;/strong&gt;&lt;/a&gt;, etc.) which are responsible for managing the project and the community around it.&lt;/p&gt;
&lt;p&gt;They can receive a lot of &lt;strong&gt;independent donations&lt;/strong&gt; or being &lt;strong&gt;funded by big companies&lt;/strong&gt; like Google or Microsoft (ie: &lt;strong&gt;Guido Van Rossum&lt;/strong&gt;, the creator of &lt;strong&gt;Python&lt;/strong&gt;, was previously working for Google and now he is currently working for Microsoft).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Big companies&lt;/strong&gt; (those earning billions every year) usually don&amp;rsquo;t have any problem spending some money to sustain the tools they also use, but this also means they &lt;strong&gt;can have a big influence on the direction&lt;/strong&gt; the project is taking.&lt;/p&gt;
&lt;p&gt;Some smaller projects (like &lt;a class=&#34;link&#34; href=&#34;https://fastapi.tiangolo.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;FastAPI&lt;/a&gt;) are being funded entirely by &lt;strong&gt;independent donations&lt;/strong&gt;: &lt;a class=&#34;link&#34; href=&#34;https://fastapi.tiangolo.com/fastapi-people/#sponsors&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://fastapi.tiangolo.com/fastapi-people/#sponsors&lt;/a&gt; but this is not a sustainable model for the long term because there is no guarantee that the donations will keep coming.&lt;/p&gt;
&lt;h3 id=&#34;the-venture-capital-model&#34;&gt;The Venture Capital model
&lt;/h3&gt;&lt;p&gt;A different way to fund open source projects is through private investors or &lt;strong&gt;venture capital&lt;/strong&gt;. For example &lt;a class=&#34;link&#34; href=&#34;https://pydantic.dev&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;pydantic&lt;/code&gt;&lt;/a&gt; (a library which is also used by FastAPI) has recently received a &lt;strong&gt;$4.7 million investment&lt;/strong&gt; from a few VCs: &lt;a class=&#34;link&#34; href=&#34;https://techcrunch.com/2023/02/16/sequoia-backs-open-source-data-validation-framework-pydantic-to-commercialize-with-cloud-services/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://techcrunch.com/2023/02/16/sequoia-backs-open-source-data-validation-framework-pydantic-to-commercialize-with-cloud-services/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Another quite recent example (which has sparked a lot of discussions in the Python community) is the case of &lt;a class=&#34;link&#34; href=&#34;https://astral.sh&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Astral&lt;/strong&gt;&lt;/a&gt;, a small company backed by VCs which are developing a couple of tools for the Python community: &lt;a class=&#34;link&#34; href=&#34;https://astral.sh/ruff&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;ruff&lt;/code&gt;&lt;/a&gt; which is a code linter and &lt;a class=&#34;link&#34; href=&#34;https://github.com/astral-sh/uv&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;uv&lt;/code&gt;&lt;/a&gt; which is a Python package and project manager written in &lt;a class=&#34;link&#34; href=&#34;https://www.rust-lang.org&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Rust&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;the-opinions-from-the-community&#34;&gt;The opinions from the Community
&lt;/h3&gt;&lt;p&gt;With specific regard to &lt;strong&gt;Astra&lt;/strong&gt;, there is an &lt;strong&gt;interesting conversation&lt;/strong&gt; happening in a &lt;a class=&#34;link&#34; href=&#34;https://social.jacobian.org/@jacob@jacobian.org@jacob@jacobian.orgian.org/113091418140504394&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Mastodon thread&lt;/a&gt;, and from what I&amp;rsquo;ve seen, the &lt;strong&gt;opinions are quite divided&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Some of them are concerned because this company is using &lt;strong&gt;Rust&lt;/strong&gt; to implement these tools. This could cut Python developers out from directly contributing to the project, because they would need to learn a new language to do so.&lt;/p&gt;
&lt;p&gt;Others are concerned because the company is backed by &lt;strong&gt;VCs&lt;/strong&gt;, and they &lt;strong&gt;could cut funds&lt;/strong&gt; at any time, leaving the project in an abandoned state. This of course is a valid concern, but since the project is open source, the community could fork it and continue the development.&lt;/p&gt;
&lt;p&gt;Another issue that seems to be mentioned by a few people is the fact that Astral doesn&amp;rsquo;t seem to have a &lt;strong&gt;clear business model&lt;/strong&gt; (at least until the time of this article). When asked directly, they have been vague about how they intend to monetize their projects and for this reason some &lt;strong&gt;people are being cautious before adopting their tools&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Some people are being optimistic instead and looking at this funding as a &lt;strong&gt;good thing&lt;/strong&gt;, because observing how quickly &lt;code&gt;ruff&lt;/code&gt; and &lt;code&gt;uv&lt;/code&gt; have evolved in so little time, they recognise &lt;strong&gt;a lot can be done when people are allowed to work full time&lt;/strong&gt; on a project.&lt;/p&gt;
&lt;h3 id=&#34;my-own-opinion&#34;&gt;My own opinion
&lt;/h3&gt;&lt;p&gt;In the specific case of &lt;code&gt;ruff&lt;/code&gt; and &lt;code&gt;uv&lt;/code&gt;, I personally don&amp;rsquo;t have anything against the fact that they are written in Rust. My mantra is &lt;strong&gt;&amp;ldquo;use the right tool for the job&amp;rdquo;&lt;/strong&gt;, and if Rust is the right tool for the job (according to their tests, linting the whole CPython code base &lt;strong&gt;only takes 0.16s&lt;/strong&gt; with &lt;code&gt;ruff&lt;/code&gt;, while it takes &lt;strong&gt;11.63s&lt;/strong&gt; with &lt;code&gt;flake8&lt;/code&gt; and even &lt;strong&gt;60s&lt;/strong&gt; with &lt;code&gt;pylint&lt;/code&gt;), then so be it.&lt;/p&gt;
&lt;p&gt;By the way, relying on &lt;strong&gt;VCs for funding is a double-edged sword&lt;/strong&gt;: on one side you now have a lot of money to hire people and let them work full time on the project. This ensures the &lt;strong&gt;development will proceed at fast pace&lt;/strong&gt; and issues will be addressed quickly. On the other side, other companies and community members could initially be attracted by the tool, decide to adopt features which are not available (nor compatible with) in other similar tools and &lt;strong&gt;eventually find themselves locked in a proprietary ecosystem&lt;/strong&gt; (check what recently happened to the data scientists community using &lt;a class=&#34;link&#34; href=&#34;https://www.theregister.com/2024/08/08/anaconda_puts_the_squeeze_on/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Anaconda&lt;/strong&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&#34;what-could-we-do-instead&#34;&gt;What could we do instead
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;I think an alternative&lt;/strong&gt; and mid ground solution to the ones described above &lt;strong&gt;exists&lt;/strong&gt;: each company should &lt;strong&gt;fund the projects&lt;/strong&gt; they use and rely on most and / or they should &lt;strong&gt;allow employees&lt;/strong&gt; to dedicate some time to &lt;strong&gt;contribute back&lt;/strong&gt; to these projects.&lt;/p&gt;
&lt;h3 id=&#34;funding-projects&#34;&gt;Funding projects
&lt;/h3&gt;&lt;p&gt;Let&amp;rsquo;s pick for example the 5 top components a company could rely on: &lt;strong&gt;Python, FastAPI, SQLAlchemy, Pydantic and PostgreSQL&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The company should allocate a yearly budget for these components and donate regularly to the projects. Many companies have no issues purchasing licenses and seats for the services they use, so why donating regularly should be a problem?&lt;/p&gt;
&lt;p&gt;By having many companies donating to a project, &lt;strong&gt;we avoid that a single company has too much influence&lt;/strong&gt; on the direction of development. If a single company decides to stop funding a component, there would be many other continuing to support it.&lt;/p&gt;
&lt;p&gt;Small projects (those with a single lead developer) could &lt;strong&gt;use the money&lt;/strong&gt; income to be able &lt;strong&gt;to work full time&lt;/strong&gt; on it. Larger projects could &lt;strong&gt;hire more people&lt;/strong&gt; to work full time. The necessary structure and organisation should reflect the size of the project itself.&lt;/p&gt;
&lt;p&gt;This would be a &lt;strong&gt;win win situation&lt;/strong&gt;. &lt;strong&gt;Developers&lt;/strong&gt; could &lt;strong&gt;work on something they love&lt;/strong&gt; and being paid for it. &lt;strong&gt;Companies&lt;/strong&gt; could trust the components they use even more because with people working full time on it, it&amp;rsquo;s more &lt;strong&gt;unlikely the project can be abandoned&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is quite similar to the funding model of &lt;a class=&#34;link&#34; href=&#34;https://www.django-rest-framework.org/#funding&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Django Rest Framework&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;allowing-employees-to-contribute-back&#34;&gt;Allowing employees to contribute back
&lt;/h3&gt;&lt;p&gt;Sometimes project owners need some direct help fixing bugs, preparing releases etc&amp;hellip; another way of contribute could be &lt;strong&gt;allowing employees to dedicate some work time to contribute back&lt;/strong&gt; directly. This can have multiple advantages: employees can &lt;strong&gt;work on something fun&lt;/strong&gt; and they can &lt;strong&gt;learn new things&lt;/strong&gt; (reminder: often employees leave a company because they want to work on more interesting stuff and because they are not learning enough in their current role) while &lt;strong&gt;projects&lt;/strong&gt; can &lt;strong&gt;get additional&lt;/strong&gt; (even if not regular) &lt;strong&gt;help&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m convinced &lt;strong&gt;a more sustainable way for funding open source projects is possible&lt;/strong&gt;. As &lt;strong&gt;software developers&lt;/strong&gt; we &lt;strong&gt;can have an high impact on companies&lt;/strong&gt; (spreading the word, speaking at conferences, leading by example etc&amp;hellip;) and this can be beneficial both for the longevity of existing projects and for the safety of companies.&lt;/p&gt;
&lt;p&gt;What do &lt;strong&gt;you&lt;/strong&gt; think about this possibility?&lt;/p&gt;
</description>
        </item>
        <item>
        <title>It&#39;s now possible to use ControlD DNS with Tailscale</title>
        <link>https://www.andreagrandi.it/posts/using-controld-dns-with-tailscale/</link>
        <pubDate>Tue, 13 Aug 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/using-controld-dns-with-tailscale/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/using-controld-dns-with-tailscale/cover-controld-tailscale.png" alt="Featured image of post It&#39;s now possible to use ControlD DNS with Tailscale" /&gt;&lt;p&gt;If you are using &lt;a class=&#34;link&#34; href=&#34;https://tailscale.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Tailscale&lt;/strong&gt;&lt;/a&gt; in some of your devices, you also need to use Tailscale own DNS server to resolve the names of the devices in the network. If you are also a user of &lt;a class=&#34;link&#34; href=&#34;https://controld.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;ControlD&lt;/strong&gt;&lt;/a&gt; DNS, and you want to use it with Tailscale, you can finally do it!&lt;/p&gt;
&lt;h2 id=&#34;configure-a-new-endpoint-on-controld&#34;&gt;Configure a new Endpoint on ControlD
&lt;/h2&gt;&lt;p&gt;First of all, you need to create a new &lt;strong&gt;endpoint&lt;/strong&gt; on ControlD. You can do it by logging in to your ControlD account, select &lt;strong&gt;Endpoints&lt;/strong&gt; from the left menu, and then click on the plus button.&lt;/p&gt;
&lt;p&gt;Scroll down to &lt;strong&gt;Router&lt;/strong&gt; section and select &lt;strong&gt;Other&lt;/strong&gt; as the device type. In the next screen you need to specify a name, I used &lt;code&gt;tailscale-global&lt;/code&gt; but you can use any name you want.&lt;/p&gt;
&lt;p&gt;Once you have created it, take note of the &lt;strong&gt;Resolver ID&lt;/strong&gt;, because you will need it in the next step.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/using-controld-dns-with-tailscale/configure-endpoint.png&#34;
	width=&#34;1665&#34;
	height=&#34;811&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Configure endpoint on ControlD - screenshot from ControlD docs&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;205&#34;
		data-flex-basis=&#34;492px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;configure-the-dns-on-tailscale&#34;&gt;Configure the DNS on Tailscale
&lt;/h2&gt;&lt;p&gt;At this point you need to go to &lt;a class=&#34;link&#34; href=&#34;https://login.tailscale.com/admin/dns&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Tailscale admin console&lt;/a&gt;, and select the &lt;strong&gt;DNS&lt;/strong&gt; tab. Click on the &lt;strong&gt;Add nameserver&lt;/strong&gt; menu, and select &amp;ldquo;Control D&amp;rdquo; from the list.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/using-controld-dns-with-tailscale/add-controld-tailscale.png&#34;
	width=&#34;1052&#34;
	height=&#34;629&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Configure Nameserver - screenshot from ControlD docs&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;167&#34;
		data-flex-basis=&#34;401px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Once you have selected &lt;strong&gt;ControlD&lt;/strong&gt; from the list, you will get this window and you will need to fill the &lt;strong&gt;Endpoint&lt;/strong&gt; using the &lt;strong&gt;Resolver ID&lt;/strong&gt; you got from the previous step.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/using-controld-dns-with-tailscale/add-controld-endpoint.png&#34;
	width=&#34;1739&#34;
	height=&#34;507&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Configure ControlD DNS - screenshot from ControlD docs&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;342&#34;
		data-flex-basis=&#34;823px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Now you can click on the &lt;strong&gt;Save&lt;/strong&gt; button.&lt;/p&gt;
&lt;h2 id=&#34;verify-the-configuration-is-working&#34;&gt;Verify the configuration is working
&lt;/h2&gt;&lt;p&gt;The configuration is done! If you already have some devices connected to your Tailscale network, you will be able to check if the configuration is working by checking the endpoints visible in the ControlD dashboard: &lt;a class=&#34;link&#34; href=&#34;https://controld.com/dashboard/endpoints&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://controld.com/dashboard/endpoints&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/using-controld-dns-with-tailscale/check-controld-endpoints.png&#34;
	width=&#34;1641&#34;
	height=&#34;446&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Check ControlD Endpoints - screenshot from ControlD docs&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;367&#34;
		data-flex-basis=&#34;883px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;If you see a &lt;strong&gt;green dot&lt;/strong&gt; next to the router icon, it means the endpoint is in use and the configuration is working.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://tailscale.com/kb/1403/control-d&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Tailscale Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.controld.com/docs/tailscale-integration&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ControlD Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Tracking airplanes with a Raspberry Pi and an ADS-B receiver</title>
        <link>https://www.andreagrandi.it/posts/tracking-airplanes-raspberrypi-adsb/</link>
        <pubDate>Sat, 10 Aug 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/tracking-airplanes-raspberrypi-adsb/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/tracking-airplanes-raspberrypi-adsb/cover-adsbexchange-flights.png" alt="Featured image of post Tracking airplanes with a Raspberry Pi and an ADS-B receiver" /&gt;&lt;p&gt;If you have ever used a flight tracking app like &lt;a class=&#34;link&#34; href=&#34;https://www.flightradar24.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Flightradar24&lt;/a&gt; or &lt;a class=&#34;link&#34; href=&#34;https://flightaware.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;FlightAware&lt;/a&gt; you might have wondered how they get the data about the airplanes in real time. All these services use an existing network of ADS-B receivers to collect data about the airplanes in the sky.&lt;/p&gt;
&lt;h2 id=&#34;what-is-ads-b&#34;&gt;What is ADS-B?
&lt;/h2&gt;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Automatic_Dependent_Surveillance%e2%80%93Broadcast&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ADS-B&lt;/a&gt; stands for &lt;strong&gt;Automatic Dependent Surveillance-Broadcast&lt;/strong&gt; and it&amp;rsquo;s a technology that allows aircraft to determine their position via satellite navigation and periodically broadcast it, enabling them to be tracked. The information can be received by air traffic control ground stations as a replacement for secondary radar.&lt;/p&gt;
&lt;p&gt;This is all &lt;strong&gt;public information&lt;/strong&gt; (it&amp;rsquo;s public data and &lt;strong&gt;you are legally allowed to receive it and share it&lt;/strong&gt;).&lt;/p&gt;
&lt;h2 id=&#34;what-do-you-need&#34;&gt;What do you need?
&lt;/h2&gt;&lt;p&gt;To build the tracking device you need a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;Raspberry Pi&lt;/strong&gt; (I&amp;rsquo;m using a Raspberry Pi 2 Model B)&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;ADS-B receiver&lt;/strong&gt; (I&amp;rsquo;m using a &lt;a class=&#34;link&#34; href=&#34;https://www.amazon.it/dp/B07K47P7XD?psc=1&amp;amp;ref=ppx_yo2ov_dt_b_product_details&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;AirNav RadarBox FlightStick&lt;/a&gt; but any &lt;strong&gt;RTL2832/R820T2-based&lt;/strong&gt; USB dongle should work)&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;1090MHz capable antenna&lt;/strong&gt; (I&amp;rsquo;m using a &lt;a class=&#34;link&#34; href=&#34;https://www.amazon.it/dp/B07ZH5FJBW?psc=1&amp;amp;ref=ppx_yo2ov_dt_b_product_details&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Bingfu Antenna RTL SDR&lt;/a&gt;, any similar antenna should work, just make sure the connector is compatible with your receiver otherwise you might need an adapter)&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;microSD card&lt;/strong&gt; (I&amp;rsquo;m using a 16GB one) and a &lt;strong&gt;power supply&lt;/strong&gt; for the Raspberry Pi&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/tracking-airplanes-raspberrypi-adsb/raspberrypi-receiver.png&#34;
	width=&#34;5712&#34;
	height=&#34;4284&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;RaspberryPi 2 and ADS-B receiver&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;is-a-raspberry-pi-x-enough&#34;&gt;Is a Raspberry Pi x enough?
&lt;/h3&gt;&lt;p&gt;I&amp;rsquo;m using a &lt;strong&gt;Raspberry Pi 2 Model B&lt;/strong&gt;, which is quite old (it was released in 2015) and it&amp;rsquo;s not the most powerful Raspberry Pi available. It&amp;rsquo;s still enough to run the software needed to track the airplanes, but if you have a newer Raspberry Pi (like a Raspberry Pi 4 or 5) you may be able to handle higher workloads (especially if you live nearby a busy airport) and feed multiple services.&lt;/p&gt;
&lt;p&gt;If you are still not sure, have a look at the stats of my Raspberry Pi 2 while running the software: I&amp;rsquo;m using &lt;strong&gt;just 23% of the CPU&lt;/strong&gt; and &lt;strong&gt;~200MB&lt;/strong&gt; of the total available &lt;strong&gt;memory&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/tracking-airplanes-raspberrypi-adsb/raspberrypi-stats.png&#34;
	width=&#34;1318&#34;
	height=&#34;753&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;RaspberryPi 2 stats&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;175&#34;
		data-flex-basis=&#34;420px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;setting-up-the-raspberry-pi&#34;&gt;Setting up the Raspberry Pi
&lt;/h2&gt;&lt;p&gt;Once you have all the components, you have two options to set up the Raspberry Pi:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;install the software from scratch&lt;/strong&gt; (this is the most complicated way but it gives you more control over the system)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;use a pre-built image&lt;/strong&gt; (this is the easiest way, you just need to flash the image on the microSD card and you are ready to go) like the one provided by &lt;a class=&#34;link&#34; href=&#34;https://www.flightradar24.com/build-your-own&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;FlightRadar24&lt;/a&gt; or &lt;a class=&#34;link&#34; href=&#34;https://www.flightaware.com/adsb/piaware/build/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Flightaware&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I won&amp;rsquo;t cover the detailed instructions, but I opted for the first option (also because I wasn&amp;rsquo;t sure which service I was going to feed).&lt;/p&gt;
&lt;p&gt;Something I can definitely suggest, in case you plan to install RaspbianOS from scratch, is to use the &lt;a class=&#34;link&#34; href=&#34;https://www.raspberrypi.com/software/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Raspberry Pi Imager&lt;/strong&gt;&lt;/a&gt; and to install the &lt;strong&gt;Lite version&lt;/strong&gt; of the OS (you don&amp;rsquo;t need the desktop environment for this project).&lt;/p&gt;
&lt;p&gt;I also suggest to &lt;strong&gt;install the &lt;a class=&#34;link&#34; href=&#34;https://tailscale.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Tailscale&lt;/a&gt; client&lt;/strong&gt; and &lt;a class=&#34;link&#34; href=&#34;https://tailscale.com/kb/1193/tailscale-ssh&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;enable SSH access&lt;/strong&gt;&lt;/a&gt; to the Raspberry Pi so you will be able to easily access the RaspberryPi from your computer, regardless of the network you are connected to.&lt;/p&gt;
&lt;h3 id=&#34;where-to-place-the-antenna&#34;&gt;Where to place the antenna?
&lt;/h3&gt;&lt;p&gt;The antenna should be placed in a &lt;strong&gt;location where it has a clear view of the sky&lt;/strong&gt;. The higher you can place it, the better the reception will be. I placed mine on the window sill, but if you have a balcony or a terrace you can place it there. To facilitate the installation of the device, I strongly recommend to at least use a WiFi connection for the Raspberry Pi, so all you need is a power supply and you just have to pass the antenna cable through the window.&lt;/p&gt;
&lt;h2 id=&#34;configuring-the-feeder&#34;&gt;Configuring the feeder
&lt;/h2&gt;&lt;p&gt;Once you have the Raspberry Pi up and running, you need to install the software to &lt;strong&gt;receive&lt;/strong&gt; the data from the ADS-B receiver and &lt;strong&gt;feed&lt;/strong&gt; it to the services.&lt;/p&gt;
&lt;p&gt;The instructions for this step depend on the service you want to feed. In my case I decided to feed to &lt;a class=&#34;link&#34; href=&#34;https://www.adsbexchange.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;ADS-B Exchange&lt;/strong&gt;&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://www.flightradar24.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Flightradar24&lt;/strong&gt;&lt;/a&gt;, which they booth provide detailed instructions and scripts to install the software:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.adsbexchange.com/ways-to-join-the-exchange/existing-equipment/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ADS-B Exchange&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.flightradar24.com/share-your-data&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Flightradar24&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; it&amp;rsquo;s worth saying that if you opt for the easier setup (using a pre-built image) you won&amp;rsquo;t have to install the software manually, but you will still need to configure the feeder to send the data to the services (by answering a few questions during the setup, about your location and type of receiver).&lt;/p&gt;
&lt;h3 id=&#34;add-nice-stats-to-your-raspberry-pi&#34;&gt;Add nice stats to your Raspberry Pi
&lt;/h3&gt;&lt;p&gt;If you want to have some nice stats like the ones I showed you before, you can install install and utility called &lt;a class=&#34;link&#34; href=&#34;https://github.com/wiedehopf/graphs1090#graphs1090&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;graphs1090&lt;/strong&gt;&lt;/a&gt;. This utility doesn&amp;rsquo;t just show you CPU and memory stats, but also data from the ADS-B receiver like the number of messages received, the number of aircrafts tracked, and the number of positions reported.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/tracking-airplanes-raspberrypi-adsb/adsb-performances.png&#34;
	width=&#34;1322&#34;
	height=&#34;845&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;ADS-B Performances - graphs1090&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;156&#34;
		data-flex-basis=&#34;375px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;which-other-services-could-or-i-should-feed&#34;&gt;Which other services could or I should feed?
&lt;/h2&gt;&lt;p&gt;There are a few other serviced you could feed, and it&amp;rsquo;s totally up to you which one you want to use. Just keep in mind these two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;each service you feed will likely give you back a &lt;strong&gt;premium account&lt;/strong&gt;, which will give you access to more features and data. So if you are interested in a specific service, you might want to feed it.&lt;/li&gt;
&lt;li&gt;most of the services are &amp;ldquo;censored&amp;rdquo; (didn&amp;rsquo;t I say the data is public?) and they don&amp;rsquo;t show all the flights. If you want to see all the flights you need to feed to &lt;a class=&#34;link&#34; href=&#34;https://www.adsbexchange.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;ADS-B Exchange&lt;/strong&gt;&lt;/a&gt; which is the only service that shows all the flights, regardless of the aircraft owner 😏&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are a few other services you could feed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://flightaware.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;FlightAware&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.radarbox24.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;RadarBox24&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://planefinder.net&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Planefinder&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://opensky-network.org&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;OpenSky Network&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; some of these services may also send you a &lt;strong&gt;free ADS-B receiver&lt;/strong&gt; or a complete kit if you provide them with a good location to place the receiver (but if you don&amp;rsquo;t live in a location they are interested in, they won&amp;rsquo;t send you anything).&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;With a total budget of around &lt;strong&gt;120€&lt;/strong&gt; (Raspberry Pi 2 Model B, AirNav RadarBox FlightStick, Bingfu Antenna RTL SDR) and a couple of hours available, you can build your own flight tracking device and feed multiple services. It&amp;rsquo;s a fun project and it&amp;rsquo;s also useful to track the flights in your area. I didn&amp;rsquo;t know anything about ADS-B receivers before a couple of weeks ago and I totally have to &amp;ldquo;blame&amp;rdquo; my friend &lt;a class=&#34;link&#34; href=&#34;https://popey.com/blog/about/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Alan Pope&lt;/strong&gt;&lt;/a&gt; who told me about this project 😅&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&lt;/h2&gt;&lt;p&gt;Here are other useful resources I found while setting up the Raspberry Pi:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.raspberrypi.com/tutorials/build-your-own-raspberry-pi-flight-tracker/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Build your own Raspberry Pi flight tracker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/wiedehopf/adsb-wiki&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ADSB Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Self hosting a website analytics at zero cost</title>
        <link>https://www.andreagrandi.it/posts/self-hosting-analytics-at-zero-cost/</link>
        <pubDate>Wed, 31 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/self-hosting-analytics-at-zero-cost/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/self-hosting-analytics-at-zero-cost/cover-self-hosting-analytics.png" alt="Featured image of post Self hosting a website analytics at zero cost" /&gt;&lt;p&gt;I recently started using &lt;a class=&#34;link&#34; href=&#34;https://umami.is&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Umami&lt;/a&gt; (a &lt;strong&gt;privacy first&lt;/strong&gt;, &lt;strong&gt;GDPR compliant&lt;/strong&gt; solution) for this website analytics but I immediately found out that &lt;strong&gt;their free plan was not enough for my needs&lt;/strong&gt; (10k page views per month).&lt;/p&gt;
&lt;p&gt;Since it&amp;rsquo;s completely &lt;strong&gt;open source&lt;/strong&gt;, you can self host it anywhere you want.&lt;/p&gt;
&lt;p&gt;I was initially evaluating between &lt;a class=&#34;link&#34; href=&#34;https://www.andreagrandi.it/posts/buy-or-rent-raspberrypi-vs-vps&#34; &gt;buying a Raspberry Pi or renting a VPS&lt;/a&gt;, but I eventually found a third solution which comes at &lt;strong&gt;zero cost&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;requirements&#34;&gt;Requirements
&lt;/h2&gt;&lt;p&gt;Your are going to need a few accounts if you want to follow exactly these steps, but keep in mind that my exact configuration is just an example and that you are free to use any other combination of services.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/a&gt;: used to login on other services and to host a fork of Umami&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/umami-software/umami&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Umami source code&lt;/strong&gt;&lt;/a&gt;: it&amp;rsquo;s available on their GitHub account&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://vercel.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Vercel&lt;/strong&gt;&lt;/a&gt;: used to host the app itself, they have a generous free plan&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://xata.io&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Xata&lt;/strong&gt;&lt;/a&gt;: they offer a very generous (15GB) free account for managed PostgreSQL&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.cloudflare.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Cloudflare&lt;/strong&gt;&lt;/a&gt;: this service is optional, but I found it quite useful to get a nice &lt;code&gt;stats.andreagrandi.it&lt;/code&gt; which points to the deployed instance on Vercel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I won&amp;rsquo;t get into every specific detail of the next steps (like &amp;ldquo;click on the second link of this page, then click&amp;hellip;&amp;rdquo;). If you decide to self host something like this, you should have a basic understanding of how a web application works, what GitHub and a database are etc&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;github&#34;&gt;GitHub
&lt;/h2&gt;&lt;p&gt;Create a GitHub account, if you don&amp;rsquo;t have one, then go to &lt;a class=&#34;link&#34; href=&#34;https://github.com/umami-software/umami&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/umami-software/umami&lt;/a&gt; and fork their repository into your own account.&lt;/p&gt;
&lt;h3 id=&#34;fix-umami-migration-script&#34;&gt;Fix Umami migration script
&lt;/h3&gt;&lt;p&gt;There is an existing &lt;strong&gt;issue with Umami initial migration script and Xata PostgreSQL&lt;/strong&gt; instance. Umami tries to create an extension (which Xata already has) but Xata doesn&amp;rsquo;t allow the creation of new extensions in the free plan and the returned error will make the script to fail.&lt;/p&gt;
&lt;p&gt;To avoid this you need to comment &lt;a class=&#34;link&#34; href=&#34;https://github.com/umami-software/umami/blob/master/db/postgresql/migrations/01_init/migration.sql#L2&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the first line of this migration script&lt;/a&gt; (by the way, I got in touch with Xata support too and they mentioned they could eventually avoid returning an error in this case but I don&amp;rsquo;t have an estimate on when this will be done).&lt;/p&gt;
&lt;h2 id=&#34;xata&#34;&gt;Xata
&lt;/h2&gt;&lt;p&gt;Before you create the account on Vercel, I suggest you to create the Xata account. You will need the PostgreSQL connection string at hand during Vercel configuration, so it&amp;rsquo;s better to create it upfront.&lt;/p&gt;
&lt;p&gt;You can use the GitHub account to login to Xata, then create a new database and select the &lt;strong&gt;&amp;ldquo;Enable direct access to Postgres&amp;rdquo;&lt;/strong&gt; option.&lt;/p&gt;
&lt;p&gt;Xata &lt;strong&gt;free plan&lt;/strong&gt; allows you to have a database where &lt;strong&gt;you can store up to 15GB of data&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Finally copy the connection string in a safe place, you will need it later.&lt;/p&gt;
&lt;h3 id=&#34;connections-limit&#34;&gt;Connections limit
&lt;/h3&gt;&lt;p&gt;Since we are here, I suggest you also add &lt;code&gt;connection_limit=10&lt;/code&gt; to the connection string, otherwise you risk to saturate all the available connections in the free plan.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;postgresql://abc123:&amp;lt;YOUR_API_KEY&amp;gt;@eu-central-1.sql.xata.sh/umami:main?sslmode&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;require&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;connection_limit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;vercel&#34;&gt;Vercel
&lt;/h2&gt;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://vercel.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Vercel&lt;/a&gt; is an hosting service which allows you to run web applications in a &lt;strong&gt;completely managed environment&lt;/strong&gt;. You won&amp;rsquo;t have to think about OS upgrades, manually install any framework or think about security. Everything will be managed and all the options can be adjusted from a web interface.&lt;/p&gt;
&lt;p&gt;The company behind it is the same behind &lt;a class=&#34;link&#34; href=&#34;https://nodejs.org&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;NodeJS&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an account using GitHub login, so you won&amp;rsquo;t have to use a specific user/password to login.&lt;/li&gt;
&lt;li&gt;Import the fork: in my case it&amp;rsquo;s the one at &lt;a class=&#34;link&#34; href=&#34;https://github.com/andreagrandi/umami&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/andreagrandi/umami&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Configure the project adding the PostgreSQL connection string: you can find this in the Xata dashboard.&lt;/li&gt;
&lt;li&gt;Test the connection and do the first deployment.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;umami&#34;&gt;Umami
&lt;/h2&gt;&lt;p&gt;Once the deployment is completed, &lt;strong&gt;Umami should be up and running&lt;/strong&gt; at the address specified here:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/self-hosting-analytics-at-zero-cost/vercel-domains.png&#34;
	width=&#34;2594&#34;
	height=&#34;1098&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Vercel deployment&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;236&#34;
		data-flex-basis=&#34;566px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;At this point you need to change the default password as soon as possible. Login using &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;umami&lt;/code&gt; and then go to the settings page to change it.&lt;/p&gt;
&lt;h2 id=&#34;cloudflare&#34;&gt;Cloudflare
&lt;/h2&gt;&lt;p&gt;In case you already use Cloudflare (I don&amp;rsquo;t suggest you go through a configuration from scratch just to have a nice url), I will show you what I did to have my stats running on &lt;code&gt;stats.andreagrandi.it&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To do this, you need to create a new CNAME record in your Cloudflare dashboard, pointing to the Vercel deployment url, like I did here:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/self-hosting-analytics-at-zero-cost/cloudflare-domain.png&#34;
	width=&#34;1964&#34;
	height=&#34;796&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Cloudflare CNAME record&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;246&#34;
		data-flex-basis=&#34;592px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Once the record has been created, you need to go back to Vercel and add the new domain to the project settings (see the previous screenshot), so that the application will be reachable from the new domain.&lt;/p&gt;
&lt;h2 id=&#34;complete-the-configuration&#34;&gt;Complete the configuration
&lt;/h2&gt;&lt;p&gt;The rest of the instructions are just about configuring the website and adding the analytics snippet to your website. I won&amp;rsquo;t go into details here, but you can find all the information you need in the &lt;a class=&#34;link&#34; href=&#34;https://umami.is/docs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;official Umami documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;add-a-website&#34;&gt;Add a website
&lt;/h3&gt;&lt;p&gt;The first thing you have to do once you login to Umami is to add a new website. You can do this by clicking on &amp;ldquo;Settings&amp;rdquo; first, then &amp;ldquo;Websites&amp;rdquo; link in the left menu and then on the &amp;ldquo;Add website&amp;rdquo; button. You can find the complete instructions &lt;a class=&#34;link&#34; href=&#34;https://umami.is/docs/add-a-website&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;install-the-analytics-snippet-to-your-website&#34;&gt;Install the analytics snippet to your website
&lt;/h3&gt;&lt;p&gt;Then you need to copy the tracking code and add it to your website. You can find the complete instructions &lt;a class=&#34;link&#34; href=&#34;https://umami.is/docs/collect-data&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;. How to do this depends on the technology you are using to build your website, but in general it must be placed inside the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag of your website.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/self-hosting-analytics-at-zero-cost/umami-snippet.png&#34;
	width=&#34;2116&#34;
	height=&#34;804&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Umami snippet&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;263&#34;
		data-flex-basis=&#34;631px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;If you are looking for a privacy first, GDPR compliant analytics solution, you should definitely consider Umami. It&amp;rsquo;s easy to use, free and open-source, and you don&amp;rsquo;t have to worry about your data being shared with third parties. You can host it yourself and have full control over your data.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Buy or Rent? RaspberryPi vs VPS</title>
        <link>https://www.andreagrandi.it/posts/buy-or-rent-raspberrypi-vs-vps/</link>
        <pubDate>Sat, 20 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/buy-or-rent-raspberrypi-vs-vps/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/buy-or-rent-raspberrypi-vs-vps/cover-raspberrypi-vs-vps.png" alt="Featured image of post Buy or Rent? RaspberryPi vs VPS" /&gt;&lt;p&gt;Recently I was planning to self host a service I&amp;rsquo;m using and I was immediately stuck on a decision: &lt;strong&gt;should I just buy a Raspberry Pi 5 or rent a VPS?&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;requirements&#34;&gt;Requirements
&lt;/h2&gt;&lt;p&gt;Let&amp;rsquo;s starts from the basic question: &lt;strong&gt;what specs do I need to run this service?&lt;/strong&gt; Reading around, it seems that something with 4GB of RAM and 20-30 GB of disk could be enough for my needs. Good.&lt;/p&gt;
&lt;h2 id=&#34;costs&#34;&gt;Costs
&lt;/h2&gt;&lt;p&gt;A &lt;strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.raspberrypi.com/products/raspberry-pi-5/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Raspberry Pi 5&lt;/a&gt; would cost me around 100€&lt;/strong&gt; (including accessories) plus the cost of energy, while &lt;strong&gt;renting a VPS&lt;/strong&gt; with similar characteristics, &lt;strong&gt;would cost me around 4,50€/month&lt;/strong&gt; (I used &lt;a class=&#34;link&#34; href=&#34;https://www.hetzner.com/cloud/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hetzner VPS prices&lt;/a&gt; as reference).&lt;/p&gt;
&lt;p&gt;To compensate costs of purchasing a Raspberry Pi, &lt;strong&gt;I should keep running my service for at least a couple of years&lt;/strong&gt;. After that time, having purchased a Raspberry Pi would come cheaper (if you consider the total cost per month) then keep paying Hetzner.&lt;/p&gt;
&lt;h2 id=&#34;complexity&#34;&gt;Complexity
&lt;/h2&gt;&lt;p&gt;Honestly, both solutions are quite &lt;strong&gt;challenging&lt;/strong&gt; (if you want to do things in the right way) and similar in terms of administration, the main difference would be that in case of a Raspberry Pi, I would have to also include the setup of something like &lt;a class=&#34;link&#34; href=&#34;https://tailscale.com/kb/1223/funnel&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Tailscale Funnel&lt;/a&gt;, to be able to expose my device on the public Internet (my provider, Fastweb, doesn&amp;rsquo;t give public IP addresses to every user and most of them are behind a NAT).&lt;/p&gt;
&lt;p&gt;In addition to this, I would also have to do the initial &lt;strong&gt;installation of the operating system&lt;/strong&gt; and the &lt;strong&gt;network configuration&lt;/strong&gt;, but it&amp;rsquo;s surely not the most difficult part.&lt;/p&gt;
&lt;p&gt;Everything else (installing a web server, a database, configuring them, installing and running the service etc&amp;hellip;) would be exactly the same.&lt;/p&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution
&lt;/h2&gt;&lt;p&gt;Both solutions are valid and both have pros and cons. So, what should one do? The best approach is evaluate both of them and pick the one that suits our needs.&lt;/p&gt;
&lt;h2 id=&#34;raspberry-pi-buy&#34;&gt;Raspberry Pi (buy)
&lt;/h2&gt;&lt;p&gt;Let&amp;rsquo;s analyse the case we buy a &lt;strong&gt;Raspberry Pi&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&#34;pros&#34;&gt;Pros
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;You physically own the hardware&lt;/li&gt;
&lt;li&gt;It can be more fun to own an electronic device&lt;/li&gt;
&lt;li&gt;After a couple of years you basically run your service for free (apart from the electricity)&lt;/li&gt;
&lt;li&gt;Your data is definitely private&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cons&#34;&gt;Cons
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Home connection is less reliable than a VPS network&lt;/li&gt;
&lt;li&gt;If the device breaks after two years, you have to buy it from scratch&lt;/li&gt;
&lt;li&gt;A bit more complexity for the initial setup&lt;/li&gt;
&lt;li&gt;If you don&amp;rsquo;t need the service anymore after a couple of months, you are left with an expensive device collecting dust&lt;/li&gt;
&lt;li&gt;Physical hardware gets old: the same Raspberry Pi is unlikely to serve you for many years and you will need to buy a new one every few years&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;vps-rent&#34;&gt;VPS (rent)
&lt;/h2&gt;&lt;p&gt;What if we rent a &lt;strong&gt;Virtual Private server&lt;/strong&gt;?&lt;/p&gt;
&lt;h3 id=&#34;pros-1&#34;&gt;Pros
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Your service is always up (even when your local ISP fails)&lt;/li&gt;
&lt;li&gt;You don&amp;rsquo;t have an high initial cost&lt;/li&gt;
&lt;li&gt;If you decide to give up after a couple of months, you have only wasted less than 10€&lt;/li&gt;
&lt;li&gt;Easier initial setup&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cons-1&#34;&gt;Cons
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Your data won&amp;rsquo;t be private&lt;/li&gt;
&lt;li&gt;If you keep running the service after 2 years (or the equivalent considering the cost of the Raspberry Pi) you also keep paying&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;As you can see, &lt;strong&gt;there is no right or wrong solution&lt;/strong&gt;. Depending on our own priorities and preferences, we may choose to buy something or to rent.&lt;/p&gt;
&lt;p&gt;What did I end up choosing for my specific case? Well&amp;hellip; in the end &lt;strong&gt;I found a third solution&lt;/strong&gt; which has some of the benefits of both, it was much easier to configure (I got it up and running in less than 1 hour) and most importantly &lt;strong&gt;costed me 0€&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;But I will talk about it in another post 😉&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Python: it is now() time to migrate from utcnow()</title>
        <link>https://www.andreagrandi.it/posts/python-now-time-to-migrate-from-utcnow/</link>
        <pubDate>Wed, 17 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/python-now-time-to-migrate-from-utcnow/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/python-now-time-to-migrate-from-utcnow/cover-python-utcnow.png" alt="Featured image of post Python: it is now() time to migrate from utcnow()" /&gt;&lt;p&gt;This morning I randomly found this post from &lt;strong&gt;Miguel Grinberg&lt;/strong&gt;: &lt;a class=&#34;link&#34; href=&#34;https://blog.miguelgrinberg.com/post/it-s-time-for-a-change-datetime-utcnow-is-now-deprecated&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;It&amp;rsquo;s Time For A Change: datetime.utcnow() Is Now Deprecated&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main point is that Python &lt;code&gt;utcnow()&lt;/code&gt; method is &lt;strong&gt;not timezone aware&lt;/strong&gt;, and &lt;strong&gt;Python 3.12 is deprecating it&lt;/strong&gt;. Therefore, you should start migrating your code to use &lt;code&gt;now()&lt;/code&gt; instead.&lt;/p&gt;
&lt;h2 id=&#34;current-state-until-python-311&#34;&gt;Current state until Python 3.11
&lt;/h2&gt;&lt;p&gt;Until Python 3.11, the &lt;code&gt;utcnow()&lt;/code&gt; method returns a &lt;code&gt;datetime&lt;/code&gt; object, and you would use it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;datetime&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utcnow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2024&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;835551&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The issue with this method is that &lt;strong&gt;it doesn&amp;rsquo;t include timezone information&lt;/strong&gt;, so you can&amp;rsquo;t be certain if the time is in UTC or not.&lt;/p&gt;
&lt;h2 id=&#34;changes-in-python-312&#34;&gt;Changes in Python 3.12
&lt;/h2&gt;&lt;p&gt;In Python 3.12, the &lt;code&gt;utcnow()&lt;/code&gt; method is being deprecated. The new method to use is &lt;code&gt;now()&lt;/code&gt;, with the appropriate parameter to make it timezone aware:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;datetime&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;timezone&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timezone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2024&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;831261&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tzinfo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timezone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;start-migrating-now&#34;&gt;Start migrating now
&lt;/h2&gt;&lt;p&gt;The good news is that &lt;strong&gt;you can start migrating your code now&lt;/strong&gt;, even if you are not using Python 3.12 yet. This way, when you upgrade to Python 3.12, you won&amp;rsquo;t have to worry about this change.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>My curated list of indie blogs</title>
        <link>https://www.andreagrandi.it/posts/my-curated-list-of-indie-blogs/</link>
        <pubDate>Fri, 12 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/my-curated-list-of-indie-blogs/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/my-curated-list-of-indie-blogs/cover-blog-list.webp" alt="Featured image of post My curated list of indie blogs" /&gt;&lt;p&gt;Recently I finally restarted to read indie blogs more regularly. To do that &lt;a class=&#34;link&#34; href=&#34;https://www.andreagrandi.it/posts/simple-solution-to-read-rss-content-on-ios-macos&#34; &gt;I&amp;rsquo;m using NetNewsWire&lt;/a&gt;, a free and open-source RSS reader for macOS and iOS and every time somone posts an interesting article, for example on Mastodon, I make sure to follow the RSS feed of that blog.&lt;/p&gt;
&lt;p&gt;In a few weeks, I&amp;rsquo;ve collected a list of blogs that I follow and I thought it would be nice to share it with you. I&amp;rsquo;m trying to read less mainstream news and more personal blogs, because I think that the personal touch of a blog is what makes it interesting and unique.&lt;/p&gt;
&lt;h2 id=&#34;some-of-the-blogs-i-follow&#34;&gt;Some of the blogs I follow
&lt;/h2&gt;&lt;p&gt;Here are some of the blogs I follow and recommend. I try to read all the blogs I follow, but these surely are the ones I read more often:&lt;/p&gt;
&lt;h3 id=&#34;brain-barking&#34;&gt;Brain Barking
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Author&lt;/strong&gt;: &lt;a class=&#34;link&#34; href=&#34;https://brainbarking.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;dr. Wouter Groeneveld&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A blog about tech, games and food.&lt;/p&gt;
&lt;h3 id=&#34;jeff-tripletts-microblog&#34;&gt;Jeff Triplett&amp;rsquo;s Micro.blog
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Author&lt;/strong&gt;: &lt;a class=&#34;link&#34; href=&#34;https://micro.webology.dev&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Jeff Triplett&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jeff is a DjangoCon organiser and a Python Software Foundation Fellow. He writes about Django, Python, and other tech topics.&lt;/p&gt;
&lt;h3 id=&#34;juha-matti-santala&#34;&gt;Juha-Matti Santala
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Author&lt;/strong&gt;: &lt;a class=&#34;link&#34; href=&#34;https://hamatti.org&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Juha-Matti Santala&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Juhis writes a lot! About tech, life, and about writing. He often inspires me to write more.&lt;/p&gt;
&lt;h3 id=&#34;terence-edens-blog&#34;&gt;Terence Eden&amp;rsquo;s Blog
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Author&lt;/strong&gt; &lt;a class=&#34;link&#34; href=&#34;https://shkspr.mobi/blog&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Terence Eden&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Terence writes about tech, privacy, and other interesting topics.
He often writes about very original gadgets and services and I always learn something new from him.
Bonus point, he&amp;rsquo;s also a very nice person!&lt;/p&gt;
&lt;h3 id=&#34;full-list&#34;&gt;Full list
&lt;/h3&gt;&lt;p&gt;You can find the full list at this address: &lt;a class=&#34;link&#34; href=&#34;https://gist.github.com/andreagrandi/3226460db523a4c60e72f22e9ad896e9&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://gist.github.com/andreagrandi/3226460db523a4c60e72f22e9ad896e9&lt;/a&gt; or you can download the OPML file from &lt;a class=&#34;link&#34; href=&#34;curated-blog-list.opml&#34; &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;tell-me-about-your-blog&#34;&gt;Tell me about your blog!
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m always looking for new blogs to follow, especially if they are indie blogs. If you have a blog and you think it&amp;rsquo;s worth reading, &lt;strong&gt;please let me know&lt;/strong&gt; either as a reply on Mastodon, or leaving a comment on the &lt;a class=&#34;link&#34; href=&#34;https://gist.github.com/andreagrandi/3226460db523a4c60e72f22e9ad896e9&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gist&lt;/a&gt; on GitHub.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Cryptomator: end-to-end encrypt files in any cloud</title>
        <link>https://www.andreagrandi.it/posts/cryptomator-end-to-end-encrypt-files-in-cloud/</link>
        <pubDate>Mon, 08 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/cryptomator-end-to-end-encrypt-files-in-cloud/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/cryptomator-end-to-end-encrypt-files-in-cloud/man-vault.png" alt="Featured image of post Cryptomator: end-to-end encrypt files in any cloud" /&gt;&lt;p&gt;If you are using a cloud storage service like &lt;strong&gt;Google Drive&lt;/strong&gt;, &lt;strong&gt;Dropbox&lt;/strong&gt;, &lt;strong&gt;OneDrive&lt;/strong&gt;, etc., you should be aware that &lt;strong&gt;your files are not encrypted by default&lt;/strong&gt;. This means that the cloud provider is able to access and, in some cases, even share them with third parties. In case your cloud provider gets hacked, your files could also be exposed to the public.&lt;/p&gt;
&lt;p&gt;To prevent this from happening, you can use a tool like &lt;a class=&#34;link&#34; href=&#34;https://cryptomator.org&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;Cryptomator&lt;/strong&gt;&lt;/a&gt; to automatically encrypt your files before uploading them to the cloud.&lt;/p&gt;
&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work?
&lt;/h2&gt;&lt;p&gt;Cryptomator is a &lt;strong&gt;free&lt;/strong&gt; and &lt;strong&gt;open-source&lt;/strong&gt; software that allows you to create an encrypted vault on your computer where you can store your files. Once you have added your files to the vault, Cryptomator will encrypt them using strong encryption algorithms (256 bit AES).&lt;/p&gt;
&lt;p&gt;To be more clear, &lt;strong&gt;Cryptomator doesn&amp;rsquo;t upload your files&lt;/strong&gt;. Your cloud provider application does:&lt;/p&gt;
&lt;p&gt;You write files to the Cryptomator vault -&amp;gt; Cryptomator encrypts them and writes them in a folder which is synced up to your cloud provider -&amp;gt; Your cloud provider application uploads the encrypted files to the cloud.&lt;/p&gt;
&lt;p&gt;When you want to access your files, you simply access them from your mounted vault and Cryptomator will automatically decrypt them for you.&lt;/p&gt;
&lt;p&gt;Once the application is installed, you will see a new volume in your file manager and you will be able to work with your files as if they were on your file system. The cloud provider will only see the encrypted version of them:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/cryptomator-end-to-end-encrypt-files-in-cloud/split-screenshots.png&#34;
	width=&#34;1664&#34;
	height=&#34;975&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;170&#34;
		data-flex-basis=&#34;409px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;what-systems-are-supported&#34;&gt;What systems are supported?
&lt;/h2&gt;&lt;p&gt;Cryptomator is available for most operating systems, including &lt;strong&gt;Windows&lt;/strong&gt;, &lt;strong&gt;MacOS&lt;/strong&gt;, &lt;strong&gt;Linux&lt;/strong&gt;, &lt;strong&gt;Android&lt;/strong&gt; and &lt;strong&gt;iOS&lt;/strong&gt;. This means that you can use it on your desktop computer, laptop, smartphone or tablet. The only limitation is that your cloud provider must have an application for the platform you are using, so you can keep your encrypted files in sync.&lt;/p&gt;
&lt;p&gt;I personally use it to keep my files safe on &lt;strong&gt;pCloud&lt;/strong&gt; and I access them from my Mac, iPhone and iPad. Yes, I&amp;rsquo;m aware that pCloud has its own encryption system, but it comes at an additional cost to the user. I also think that it&amp;rsquo;s safer if they don&amp;rsquo;t have the encryption keys, in case they get hacked.&lt;/p&gt;
&lt;h2 id=&#34;how-much-does-it-cost&#34;&gt;How much does it cost?
&lt;/h2&gt;&lt;p&gt;Cryptomator is &lt;strong&gt;free&lt;/strong&gt; and &lt;strong&gt;open-source&lt;/strong&gt;, which means that you can use it without paying anything. The only application that is not free is the one for iOS, which costs a few dollars if you want to unlock all the features (for example the ability to write files), but you can still use it for free to read files (and there is a demo version available too).&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;If you are using a cloud storage service and you want to keep your files safe and secure, you should definitely consider using Cryptomator. It is easy to use, free and open-source, and you don&amp;rsquo;t have to worry about your cloud provider being 100% safe, because even if your files get leaked, they will be encrypted and no one will be able to read them without the encryption key.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>If your website uses Cloudflare, you can now easily block AI bots</title>
        <link>https://www.andreagrandi.it/posts/use-cloudflare-to-block-ai-bots/</link>
        <pubDate>Sat, 06 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/use-cloudflare-to-block-ai-bots/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/use-cloudflare-to-block-ai-bots/cloudflare-ai-bots-settings.png" alt="Featured image of post If your website uses Cloudflare, you can now easily block AI bots" /&gt;&lt;p&gt;If you own a website and you don&amp;rsquo;t want AI bots to crawl it and you are already using Cloudflare, you are in luck. &lt;strong&gt;Cloudflare&lt;/strong&gt; has recently added a new feature that allows people to &lt;strong&gt;easily block AI bots&lt;/strong&gt; from crawling their website.&lt;/p&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup
&lt;/h2&gt;&lt;p&gt;To enable this feature, you need to login to your Cloudflare account, select the website and once you are in the dashboard, go to the &lt;strong&gt;&amp;ldquo;Security&amp;rdquo;&lt;/strong&gt; section and finally click on the &lt;strong&gt;&amp;ldquo;Bots&amp;rdquo;&lt;/strong&gt; link.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/use-cloudflare-to-block-ai-bots/cloudflare-ai-bots-settings-menu.png&#34;
	width=&#34;274&#34;
	height=&#34;328&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;83&#34;
		data-flex-basis=&#34;200px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;From there you need to enable both &lt;strong&gt;&amp;ldquo;Bot Fight Mode&amp;rdquo;&lt;/strong&gt; and &lt;strong&gt;&amp;ldquo;Block AI Scrapers and Crawlers&amp;rdquo;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Yes, it&amp;rsquo;s that simple! And you will be happy to know that this feature is &lt;strong&gt;available to all Cloudflare users&lt;/strong&gt;, even those on the &lt;strong&gt;free&lt;/strong&gt; plan.&lt;/p&gt;
&lt;h2 id=&#34;does-it-really-work&#34;&gt;Does it really work?
&lt;/h2&gt;&lt;p&gt;According to Cloudflare logs, there seem to be a lot of AI bots trying to crawl my website. Here you can see just a few of them:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/use-cloudflare-to-block-ai-bots/cloudflare-logs-bots.png&#34;
	width=&#34;1038&#34;
	height=&#34;816&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;127&#34;
		data-flex-basis=&#34;305px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;In particular you can see a bot coming from Singapore and recognised as &lt;strong&gt;Bytedance&lt;/strong&gt; (the same company that owns TikTok) which are well known for their &lt;a class=&#34;link&#34; href=&#34;https://www.fastcompany.com/90992383/bytedance-tiktok-generative-ai-chatbot-us-regulation&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;scraping activities&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/use-cloudflare-to-block-ai-bots/cloudflare-logs-details-bytedance.png&#34;
	width=&#34;1038&#34;
	height=&#34;529&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;196&#34;
		data-flex-basis=&#34;470px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Other AI companies, like &lt;strong&gt;PerplexityAI&lt;/strong&gt;, seem to have found a way around these blocks, because if you try to ask Perplexity to summarise one of the pages of this website, it will still be able to do it, but I&amp;rsquo;m sure that Cloudflare will soon find a way to block them too.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;I think people should be able to decide if they want their content to be used by AI models or not and Cloudflare is now providing an easy option to do that. AI tools are not bad per se, but they should not be built using other people&amp;rsquo;s content without their &lt;strong&gt;consent&lt;/strong&gt;.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>We need an evolved robots.txt and regulations to enforce it</title>
        <link>https://www.andreagrandi.it/posts/we-need-evolved-robotstxt-and-regulations/</link>
        <pubDate>Sat, 22 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/we-need-evolved-robotstxt-and-regulations/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/we-need-evolved-robotstxt-and-regulations/we-need-new-robots.png" alt="Featured image of post We need an evolved robots.txt and regulations to enforce it" /&gt;&lt;p&gt;The &lt;code&gt;robots.txt&lt;/code&gt; file is a simple text file that tells web robots (like search engine crawlers) which pages on your site to crawl and which not to crawl. It&amp;rsquo;s a &lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Robots.txt&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;standard&lt;/a&gt; that has been around for a long time and it&amp;rsquo;s still used today.&lt;/p&gt;
&lt;p&gt;Some examples of rules you can put in a &lt;code&gt;robots.txt&lt;/code&gt; file are:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: *
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disallow: /private/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This rule tells all web robots to not crawl the &lt;code&gt;/private/&lt;/code&gt; directory.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;User-agent: Googlebot
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disallow: /users/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This rule tells Googlebot to not crawl the &lt;code&gt;/users/&lt;/code&gt; directory.&lt;/p&gt;
&lt;h2 id=&#34;then-ai-came&#34;&gt;Then AI came
&lt;/h2&gt;&lt;p&gt;In the age of AI, the existing &lt;code&gt;robots.txt&lt;/code&gt; specification is not enough to express the rules for web crawlers. We can only tell agent if they can or cannot crawl a certain path, but we cannot express more complex rules.&lt;/p&gt;
&lt;p&gt;In my opinion we should be able to express more detailed rules, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Indexing:&lt;/strong&gt; should a web crawler be able to index the content?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching:&lt;/strong&gt; should a web crawler be able to cache the content?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM Training:&lt;/strong&gt; should a web crawler be able to use the content to train a language model?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Summarising:&lt;/strong&gt; should a web crawler be able to summarise the content?&lt;/li&gt;
&lt;li&gt;etc&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some of the above things were not possible in the past and it should be up to the website owner to decide if they want their content to be used in such ways.&lt;/p&gt;
&lt;h2 id=&#34;enforcing-the-rules&#34;&gt;Enforcing the rules
&lt;/h2&gt;&lt;p&gt;In addition to more detailed rules, &lt;strong&gt;we need new regulations to enforce them&lt;/strong&gt;. It looks like the &lt;code&gt;robots.txt&lt;/code&gt; file is not enough to stop certain companies from doing what they want.&lt;/p&gt;
&lt;p&gt;As someone &lt;a class=&#34;link&#34; href=&#34;https://rknight.me/blog/perplexity-ai-is-lying-about-its-user-agent/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;recently found out&lt;/a&gt;, &lt;strong&gt;Perplexity AI is using a fake user agent to crawl websites&lt;/strong&gt;, pretending to be a regular user. This is a clear violation of the rules specified in &lt;code&gt;robots.txt&lt;/code&gt; file. This claim has recently been &lt;a class=&#34;link&#34; href=&#34;https://www.wired.com/story/perplexity-is-a-bullshit-machine/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;confirmed by Wired&lt;/a&gt; and by &lt;a class=&#34;link&#34; href=&#34;https://www.macstories.net/stories/wired-confirms-perplexity-is-bypassing-efforts-by-websites-to-block-its-web-crawler/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;MacStories&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;As we have seen, having good rules is not enough if they are not enforced. In particular we need regulators to take care of complaints from content owners and &lt;strong&gt;fine companies that do not respect the rules&lt;/strong&gt; (like Perplexity AI), because small content creators cannot afford to take legal actions against big companies.&lt;/p&gt;
&lt;p&gt;As with every single thing, it&amp;rsquo;s never &amp;ldquo;the tool&amp;rdquo;, but rather &amp;ldquo;how you use it&amp;rdquo;. AI itself can bring innovation in certain fields, but this can&amp;rsquo;t be done at the expense of other people&amp;rsquo;s work and rights.&lt;/p&gt;
&lt;h3 id=&#34;disclaimer&#34;&gt;Disclaimer
&lt;/h3&gt;&lt;p&gt;Yes, of course the cover image has been generated with AI. It&amp;rsquo;s far from perfect, but it&amp;rsquo;s still better than my drawing skills. The content of this article instead is 100% human generated.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Simple and free solution to read content from RSS feeds on iOS and MacOS</title>
        <link>https://www.andreagrandi.it/posts/simple-solution-to-read-rss-content-on-ios-macos/</link>
        <pubDate>Tue, 04 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/simple-solution-to-read-rss-content-on-ios-macos/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/simple-solution-to-read-rss-content-on-ios-macos/rss-sync-cover.webp" alt="Featured image of post Simple and free solution to read content from RSS feeds on iOS and MacOS" /&gt;&lt;p&gt;Reading news and blog posts can be quite time consuming if we manually go to news websites to check if something new is out or if we spend all day checking social media, waiting for people we follow to post their latest article.&lt;/p&gt;
&lt;p&gt;An efficient way to fetch new content is to follow the news sites and the blogs we want, using their &lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/RSS&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;RSS&lt;/a&gt; feed.&lt;/p&gt;
&lt;p&gt;What is the best way to do this for iOS and MacOS? &lt;strong&gt;There is no &amp;ldquo;best&amp;rdquo; way&lt;/strong&gt;, since there are plenty of RSS clients and combinations of services, but I will tell you a simple and free solution I&amp;rsquo;m using.&lt;/p&gt;
&lt;h2 id=&#34;netnewswire-and-icloud-sync&#34;&gt;NetNewsWire and iCloud sync
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://www.andreagrandi.it/posts/simple-solution-to-read-rss-content-on-ios-macos/netnewsfeed-main.png&#34;
	width=&#34;2360&#34;
	height=&#34;1640&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;143&#34;
		data-flex-basis=&#34;345px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The client I&amp;rsquo;m using is &lt;a class=&#34;link&#34; href=&#34;https://netnewswire.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NetNewsWire&lt;/a&gt;. It&amp;rsquo;s &lt;strong&gt;free&lt;/strong&gt;, &lt;strong&gt;open source&lt;/strong&gt; and available for both &lt;strong&gt;iOS&lt;/strong&gt; and &lt;strong&gt;MacOS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This client supports different synchronisation services, including &lt;strong&gt;iCloud&lt;/strong&gt;, &lt;strong&gt;FeedBin&lt;/strong&gt; and &lt;strong&gt;Inoreader&lt;/strong&gt; or it can be used just on a single device, without using any of these services.&lt;/p&gt;
&lt;p&gt;I initially tried using Inoreader and it was &amp;ldquo;ok&amp;rdquo;, but it&amp;rsquo;s a paid service which injects ads in its free plan. Also, its cheaper paid plan only removes ads, without offering any additional value. I really couldn&amp;rsquo;t justify paying a subscription just to keep my feeds synced across devices, so &lt;strong&gt;I opted to use iCloud&lt;/strong&gt; which, in my case, is exactly what I need and I don&amp;rsquo;t have to purchase any additional plan (I&amp;rsquo;m already on a paid plan, but even the free plan is enough for this).&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m sure there are better solutions around (especially RSS clients) and I would be glad to hear about them, but for now this simple client and iCloud is all I need to enjoy the news and read blog posts.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Truncating time when querying a PostgreSQL database</title>
        <link>https://www.andreagrandi.it/posts/truncating-time-in-postgresql-query/</link>
        <pubDate>Sat, 18 May 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/truncating-time-in-postgresql-query/</guid>
        <description>&lt;p&gt;When you have to compare dates in a SQL query, you often want to ignore the time part. For example if you have two very common fields like &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; and you want to know if they are the same day, you have to &lt;strong&gt;truncate the time part&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;How to do this depends on the database you are using. Here is how you can do it in &lt;strong&gt;PostgreSQL&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;DATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;DATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;updated_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This query will return all the users where the &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; fields are not on the same day, ignoring the time part.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>AI won’t take your Software Developer job (not yet)</title>
        <link>https://www.andreagrandi.it/posts/ai-wont-take-your-job-yet/</link>
        <pubDate>Wed, 01 May 2024 00:00:00 +0000</pubDate>
        
        <guid>https://www.andreagrandi.it/posts/ai-wont-take-your-job-yet/</guid>
        <description>&lt;img src="https://www.andreagrandi.it/posts/ai-wont-take-your-job-yet/ai-doing-developers-work-cover.webp" alt="Featured image of post AI won’t take your Software Developer job (not yet)" /&gt;&lt;p&gt;In the last 3 days I had the opportunity to participate to an internal hackathon organised by the company I work for.&lt;/p&gt;
&lt;p&gt;We had to build a project based on an SDK and the needed skills to build it were those of a frontend developer.&lt;/p&gt;
&lt;p&gt;Being a backend developer and having very basic (almost none) frontend skills, I was suggested to use ChatGPT to write some of the code.&lt;/p&gt;
&lt;h2 id=&#34;challenge-accepted&#34;&gt;Challenge accepted
&lt;/h2&gt;&lt;p&gt;Of course I accepted and I worked on the project with another colleague (also backend developer) who also used ChatGPT to write some of the code.&lt;/p&gt;
&lt;h2 id=&#34;how-i-normally-use-chatgpt&#34;&gt;How I normally use ChatGPT
&lt;/h2&gt;&lt;p&gt;ChatGPT is not new to me. I use it daily for my work, but I mostly use it as a &amp;ldquo;search engine on steroids&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Rather than searching something on a search engine, read a few blog posts or forums, then landing on a Stack Overflow page, to find some code and adapt it, I ask the same question to ChatGPT and then I adapt the produced example.&lt;/p&gt;
&lt;p&gt;I never ask for a complete feature or even a whole method. I usually need a small example for a very specific thing which I then insert in an existing code base.&lt;/p&gt;
&lt;h2 id=&#34;how-i-used-it-for-the-hackathon&#34;&gt;How I used it for the hackathon
&lt;/h2&gt;&lt;p&gt;In the case of the hackathon, I knew what I wanted to achieve, but I had no idea how to do it, so I asked it to produce almost everything I needed.&lt;/p&gt;
&lt;p&gt;At the beginning it was easy: I had nothing and I was adding small pieces every time.&lt;/p&gt;
&lt;p&gt;After a little bit I ended up with a lot of code I couldn&amp;rsquo;t understand anymore.&lt;/p&gt;
&lt;p&gt;I was having an issue with the produced code and ChatGPT, instead of helping, started to allucinate.&lt;/p&gt;
&lt;p&gt;Instead of asking it to fix a specific issue, I was passing back the whole JavaScript file, asking for the issues to be corrected.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t notice that while fixing the issues, ChatGPT was also rewriting the whole file and in a couple of occasions it started changing the logic entirely.&lt;/p&gt;
&lt;p&gt;The errors disappeared but the code started behaving differently.&lt;/p&gt;
&lt;p&gt;In the end we had to revert to a simpler and older version of our code, which at least was working correctly.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;I remain confident that tools like ChatGPT can be valuable assets for software developers, provided they already possess a strong understanding of the relevant domain, such as Python backend development in my experience.&lt;/p&gt;
&lt;p&gt;However, for areas outside one&amp;rsquo;s expertise, such as frontend development, relying solely on ChatGPT is insufficient.&lt;/p&gt;
&lt;p&gt;What is your experience with similar tools? Let me know in the comments.&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
