<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bear Huang</title>
    <description>The latest articles on DEV Community by Bear Huang (@bear0330).</description>
    <link>https://dev.to/bear0330</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3783326%2Ffd7c2e18-8694-4502-84a7-bf993dee75d4.png</url>
      <title>DEV Community: Bear Huang</title>
      <link>https://dev.to/bear0330</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bear0330"/>
    <language>en</language>
    <item>
      <title>Using FastFileLink to Deliver a 3.8GB, 158-File U.S. Department of War UFO Public Archive</title>
      <dc:creator>Bear Huang</dc:creator>
      <pubDate>Thu, 14 May 2026 08:51:38 +0000</pubDate>
      <link>https://dev.to/bear0330/using-fastfilelink-to-deliver-a-38gb-158-file-us-department-of-war-ufo-public-archive-1f5k</link>
      <guid>https://dev.to/bear0330/using-fastfilelink-to-deliver-a-38gb-158-file-us-department-of-war-ufo-public-archive-1f5k</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu32o1pks1gpo6vhsgtxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu32o1pks1gpo6vhsgtxw.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recently, war.gov launched a public UFO/UAP records page.&lt;/p&gt;

&lt;p&gt;According to the official description, the release was part of the Trump administration's push for more transparency around UAP-related information. The U.S. Department of War (DOW), together with other agencies, published a batch of unresolved UAP-related records and historical documents.&lt;/p&gt;

&lt;p&gt;When I saw the news, my first thought was simple:&lt;/p&gt;

&lt;p&gt;“It would be really useful if people could download this whole thing in one place.”&lt;/p&gt;

&lt;p&gt;The official page is browsable, but if you actually want to download all the PDFs, videos, and images, the experience is not that smooth. You have to click through files one by one, figure out which items are documents, which are videos, and which are just images used by the page itself. For a normal user, even getting started is already a bit of work.&lt;/p&gt;

&lt;p&gt;This is exactly the kind of problem FastFileLink is meant to solve: taking a batch of files and turning it into a package that can be delivered, downloaded, and clearly explained.&lt;/p&gt;

&lt;p&gt;I was personally curious about the material too, so I spent some time collecting the files and checking whether it was legally reasonable to redistribute these public records. After confirming that it was okay to do, I decided to organize everything and make it easier for others to download.&lt;/p&gt;

&lt;p&gt;This kind of archive is a natural fit for FastFileLink. The full package is around 3.8GB and contains many files. I honestly did not have an easy place to put it. You cannot send something like this through WhatsApp, LINE, or a regular messaging app. A cloud drive would bring its own issues: storage limits, permissions, packaged downloads, bandwidth limits, and so on. So I decided to document the whole process as I went.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, I organized the public files into one complete folder&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started by collecting the directly downloadable files from the war.gov UFO/UAP Release 01 page.&lt;/p&gt;

&lt;p&gt;The final package looked roughly like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;158 downloadable files&lt;/li&gt;
&lt;li&gt;116 PDFs&lt;/li&gt;
&lt;li&gt;28 videos&lt;/li&gt;
&lt;li&gt;14 images&lt;/li&gt;
&lt;li&gt;Around 3.8GB total&lt;/li&gt;
&lt;li&gt;Source URLs preserved for every file&lt;/li&gt;
&lt;li&gt;SHA-256 checksums generated&lt;/li&gt;
&lt;li&gt;Index files and a manifest included&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I put everything into one folder:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Besides the actual files, I also prepared a few helper files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;README.md
START_GUIDE.html
files_index.csv
files_index.jsonl
manifest.json
checksum.sha256
files/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;README.md&lt;/code&gt; explains the source, scope, licensing notes, and the non-endorsement disclaimer.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;START_GUIDE.html&lt;/code&gt; is an entry guide for regular users. The release itself is quite scattered: there are videos, images, FBI-related materials, NASA/Apollo-related materials, and large historical scanned PDFs. If someone downloads the archive and only sees a pile of files, they may have no idea where to begin.&lt;/p&gt;

&lt;p&gt;So in &lt;code&gt;START_GUIDE.html&lt;/code&gt;, I added a simple suggested browsing order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with the videos&lt;/li&gt;
&lt;li&gt;Then look at the images&lt;/li&gt;
&lt;li&gt;Then open the shorter modern PDFs&lt;/li&gt;
&lt;li&gt;Then move on to the FBI and NASA/Apollo-related materials&lt;/li&gt;
&lt;li&gt;Save the large historical PDFs for last&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That gives people a straightforward entry point after downloading the archive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uploading the whole folder with FastFileLink&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the folder was ready, I uploaded it.&lt;/p&gt;

&lt;p&gt;I logged into my FastFileLink account and uploaded the entire &lt;code&gt;04_full_release01_pack&lt;/code&gt; folder. The important part here is that I did not need to compress it into a ZIP myself, and I did not need to manually handle how all the files would be packaged, listed, or delivered to downloaders.&lt;/p&gt;

&lt;p&gt;FastFileLink handled the folder delivery directly.&lt;/p&gt;

&lt;p&gt;At first, the upload produced a standard download page like this:&lt;/p&gt;

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

&lt;p&gt;This standard page is clean, functional, and downloads work just fine.&lt;/p&gt;

&lt;p&gt;But when I looked at it, it felt too neutral.&lt;/p&gt;

&lt;p&gt;This package was about the U.S. Department of War UFO/UAP public records. When someone opens the page, they should immediately understand what they are downloading. Especially for a 3.8GB package, a generic download box may not inspire enough confidence.&lt;/p&gt;

&lt;p&gt;So I decided to use FastFileLink's white-label customization feature to make the page better match the actual content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using white-label customization to turn the download page into a dedicated landing page&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;FastFileLink's white-label settings let you customize the logo, background, colors, navigation links, download notes, footer content, and more.&lt;/p&gt;

&lt;p&gt;I switched the logo to a white version because I wanted a dark theme for this page. Then I changed the background to a deep blue, tech-style image and used a cool color palette that fit the UFO/UAP topic.&lt;/p&gt;

&lt;p&gt;After the customization, the page looked more like this:&lt;/p&gt;

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

&lt;p&gt;Now, as soon as someone opens the page, they can quickly see that the package contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;U.S. Department of War UFO/UAP public records&lt;/li&gt;
&lt;li&gt;158 files&lt;/li&gt;
&lt;li&gt;116 PDFs&lt;/li&gt;
&lt;li&gt;28 videos&lt;/li&gt;
&lt;li&gt;14 images&lt;/li&gt;
&lt;li&gt;Source URLs, index files, and SHA-256 checksums&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also added source and legal notes in the footer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source: war.gov/UFO&lt;/li&gt;
&lt;li&gt;This is an unofficial convenience download pack&lt;/li&gt;
&lt;li&gt;It is not affiliated with, sponsored by, approved by, or endorsed by the U.S. Department of War, U.S. Department of Defense, AARO, NARA, DVIDS, or any U.S. government agency&lt;/li&gt;
&lt;li&gt;The appearance of U.S. government visual information does not imply DoW/DoD endorsement of FastFileLink or this website&lt;/li&gt;
&lt;li&gt;The archive is redistributed as publicly released and does not claim that the files prove extraterrestrial life&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That disclaimer matters. Since the package uses U.S. government public records, and U.S. federal government works are generally public domain in the United States, redistribution is usually not the main issue. The important thing is to avoid giving the impression of an official partnership or endorsement.&lt;/p&gt;

&lt;p&gt;There is also a small localization detail: my browser is set to Chinese, so the download progress and interface text appear in Chinese for me. English readers opening the same page will see the download interface in English according to their browser environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A download page should explain the content before asking people to download&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After I published the package, someone gave me a very practical piece of feedback:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It'd still be courteous to wait for a click before downloading 4 GB of files. I just wanted to see a description of what's included first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That made me think.&lt;/p&gt;

&lt;p&gt;From the sender's point of view, opening a download page and preparing the download right away makes sense. The goal is to deliver the files.&lt;/p&gt;

&lt;p&gt;But for someone seeing the link for the first time, 4GB is not a small download. They may simply want to check what is inside, confirm the source, review the size, and decide whether they actually want to download it.&lt;/p&gt;

&lt;p&gt;FastFileLink already has an eye icon on the page that opens a preview, where people can see the file description and content information.&lt;/p&gt;

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

&lt;p&gt;But I realized that if I am sharing a link on a forum or social platform, it is better not to send people straight into the download flow. A better approach is to share the preview-mode link directly.&lt;/p&gt;

&lt;p&gt;The trick is simple: add this to the end of the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;?preview=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://0.2.fastfilelink.com/xxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://0.2.fastfilelink.com/xxxx?preview=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, people first see the description, file information, and download notes. Then they can choose whether to start the download.&lt;/p&gt;

&lt;p&gt;For large-file delivery, this is a very useful detail. The bigger the file, the more likely the first question is not “How do I download it?” but “What exactly is this?”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How people reacted after I shared it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the package was ready, I posted it in a few places to see how people would react.&lt;/p&gt;

&lt;p&gt;I tried Hacker News first. Surprisingly, it did not get much traction and only received a small number of points. Someone later pointed out that this kind of reading material or public archive is not a great fit for a Show HN submission. There may also have already been a similar effort by someone else, though that version was mainly hosted on GitHub and required Git LFS to download. Hacker News readers know how to use Git LFS, of course, but most normal users do not.&lt;/p&gt;

&lt;p&gt;That was one of the reasons I made this package in the first place: not everyone uses Git, and not everyone has Git LFS installed. Many people just want to download the full archive through a browser.&lt;/p&gt;

&lt;p&gt;The response on V2EX was much better than I expected. A lot of people said thanks, and many people actually downloaded the package. The backend showed thousands of clicks and hundreds of real downloads.&lt;/p&gt;

&lt;p&gt;That gave me another useful confirmation: FastFileLink worked smoothly in a real scenario with a large archive, many files, and many people downloading around the same time. This was not just a local test, and it was not just one or two people opening the link. A real group of users actually downloaded the 3.8GB package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me about large-file delivery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the surface, this project looks like a quick response to a news topic.&lt;/p&gt;

&lt;p&gt;But after going through the whole process, I came away with a clearer understanding of large-file delivery. The hard part is often not just that the file is large.&lt;/p&gt;

&lt;p&gt;The real issues are things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are many files&lt;/li&gt;
&lt;li&gt;The file types are mixed&lt;/li&gt;
&lt;li&gt;Source URLs need to be preserved&lt;/li&gt;
&lt;li&gt;Downloaders need to know where to start&lt;/li&gt;
&lt;li&gt;The content needs explanation&lt;/li&gt;
&lt;li&gt;Checksums matter&lt;/li&gt;
&lt;li&gt;Legal and attribution notes matter&lt;/li&gt;
&lt;li&gt;The download page needs to feel trustworthy&lt;/li&gt;
&lt;li&gt;Previewing before download can be important&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you simply drop a folder into a cloud drive, you have to patch together all of these details yourself.&lt;/p&gt;

&lt;p&gt;FastFileLink turns delivery into a more complete workflow: upload the folder, customize the page, add the explanation, share the link, and let people download it through a browser.&lt;/p&gt;

&lt;p&gt;This public archive was a good example of that workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The final result&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the end, I prepared three versions for different audiences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;English&lt;/li&gt;
&lt;li&gt;Traditional Chinese&lt;/li&gt;
&lt;li&gt;Simplified Chinese&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each version has its own download page, README, and START_GUIDE.&lt;/p&gt;

&lt;p&gt;This was not a UFO research project, and I did not try to interpret the files. My goal was simple: take a batch of official public files and make them easier for normal people to download and start browsing.&lt;/p&gt;

&lt;p&gt;I also put the materials I created myself — the README, START_GUIDE, index files, checksums, manifest, and the three download links — on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/bear0330/war-gov-ufo-uap-release-01" rel="noopener noreferrer"&gt;https://github.com/bear0330/war-gov-ufo-uap-release-01&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The actual large files are still delivered through FastFileLink. GitHub is a good place for the index, documentation, and verification files. FastFileLink is better suited for delivering the full 3.8GB archive to normal users.&lt;/p&gt;

&lt;p&gt;If you have a similar need, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delivering a large public archive&lt;/li&gt;
&lt;li&gt;Sending a full set of video assets&lt;/li&gt;
&lt;li&gt;Handing off client project files&lt;/li&gt;
&lt;li&gt;Sharing a large collection of photos or original files&lt;/li&gt;
&lt;li&gt;Letting someone download a folder through a browser without installing extra tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;that is exactly the kind of situation FastFileLink is designed for.&lt;/p&gt;

&lt;p&gt;Sometimes file transfer is not just about sending files out.&lt;br&gt;&lt;br&gt;
Organizing the files, explaining them clearly, and helping the recipient download with confidence is what real delivery looks like.&lt;/p&gt;

</description>
      <category>data</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>tooling</category>
    </item>
    <item>
      <title>How ffl Differs from CLI File Transfer Tools Like croc and Magic Wormhole</title>
      <dc:creator>Bear Huang</dc:creator>
      <pubDate>Wed, 13 May 2026 10:18:00 +0000</pubDate>
      <link>https://dev.to/bear0330/how-ffl-differs-from-cli-file-transfer-tools-like-croc-and-magic-wormhole-i57</link>
      <guid>https://dev.to/bear0330/how-ffl-differs-from-cli-file-transfer-tools-like-croc-and-magic-wormhole-i57</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frr6lwryhcgr4cofo6znz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frr6lwryhcgr4cofo6znz.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you often move files from the terminal, you have probably heard of tools like &lt;code&gt;croc&lt;/code&gt;, Magic Wormhole, portal, or sendme. They all solve the same basic problem: getting a file from one machine to another.&lt;/p&gt;

&lt;p&gt;Most file transfer tools fall into two broad categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;P2P transfer tools&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
These try to connect the sender and receiver directly. When a direct path is not available, some of them fall back to a relay. Magic Wormhole, portal, and sendme belong to this general category. &lt;code&gt;croc&lt;/code&gt; is more relay-first, which helps avoid many NAT and firewall problems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cloud-upload tools&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
These upload the file to a server first, then give the recipient a download link. Tools and services like transfer.sh, WeTransfer, and file.io follow this model.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In everyday use, most people are already familiar with the second category: Google Drive, Dropbox, WeTransfer, and similar services. The reason is simple: the recipient does not need to install anything. They receive a link and download the file.&lt;/p&gt;

&lt;p&gt;CLI transfer tools have a different strength. They are fast, simple, automation-friendly, and great for temporary transfers. But many of them share one limitation: &lt;strong&gt;the receiver usually needs to install the same tool too&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;croc&lt;/code&gt; is very convenient, but both sides need &lt;code&gt;croc&lt;/code&gt;. Magic Wormhole also expects both sides to participate in the same wormhole workflow. That is fine when you are moving files between your own machines. It becomes less convenient when you need to send something to a client, a teammate, a phone user, or a group of people.&lt;/p&gt;

&lt;p&gt;So what makes &lt;a href="https://github.com/nuwainfo/ffl" rel="noopener noreferrer"&gt;ffl&lt;/a&gt; different?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ffl&lt;/code&gt; is the CLI version of FastFileLink. Its core idea is simple: from the terminal, turn files, folders, or even stdin streams into an HTTPS download link that anyone can open.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main difference: ffl gives the receiver an HTTPS link
&lt;/h2&gt;

&lt;p&gt;The mental model of &lt;code&gt;ffl&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffl file.zip
→ creates an HTTPS link
→ the recipient downloads it with a browser, curl, wget, or ffl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recipient does not need to install &lt;code&gt;ffl&lt;/code&gt; first. If they have a browser, they can download the file. If they are on a server, they can use &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;wget&lt;/code&gt;. This changes the shape of the whole transfer flow.&lt;/p&gt;

&lt;p&gt;Most CLI transfer tools are built around a paired session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sender terminal  &amp;lt;-&amp;gt;  receiver terminal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ffl&lt;/code&gt; is closer to creating a reusable delivery point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sender creates an HTTPS link
recipient A downloads
recipient B downloads
recipient C downloads later
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is why &lt;code&gt;ffl&lt;/code&gt; naturally fits one-to-many delivery, repeated downloads, and resumable downloads. It is useful for “send this from one of my machines to another,” and it is also built for “deliver this file to someone else.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick comparison
&lt;/h2&gt;

&lt;p&gt;✅ Supported / suitable　❌ Not supported / not suitable　➖ Depends, or not the main workflow&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key point&lt;/th&gt;
&lt;th&gt;ffl&lt;/th&gt;
&lt;th&gt;croc&lt;/th&gt;
&lt;th&gt;Magic Wormhole&lt;/th&gt;
&lt;th&gt;sendme&lt;/th&gt;
&lt;th&gt;transfer.sh / WeTransfer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Receiver needs to install a tool&lt;/td&gt;
&lt;td&gt;✅ No, browser is enough&lt;/td&gt;
&lt;td&gt;❌ Yes&lt;/td&gt;
&lt;td&gt;❌ Yes&lt;/td&gt;
&lt;td&gt;❌ Yes&lt;/td&gt;
&lt;td&gt;✅ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Download with &lt;code&gt;curl&lt;/code&gt; / &lt;code&gt;wget&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main handoff model&lt;/td&gt;
&lt;td&gt;✅ HTTPS link&lt;/td&gt;
&lt;td&gt;🔑 Code phrase&lt;/td&gt;
&lt;td&gt;🔑 Code phrase&lt;/td&gt;
&lt;td&gt;🎫 Ticket / CLI&lt;/td&gt;
&lt;td&gt;☁️ Cloud link&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-to-many delivery&lt;/td&gt;
&lt;td&gt;✅ Natural&lt;/td&gt;
&lt;td&gt;❌ Not suitable&lt;/td&gt;
&lt;td&gt;❌ Not suitable&lt;/td&gt;
&lt;td&gt;❌ Not suitable&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repeated downloads from the same share&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;➖ Not the main model&lt;/td&gt;
&lt;td&gt;➖ Not the main model&lt;/td&gt;
&lt;td&gt;➖ Not the main model&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Direct P2P&lt;/td&gt;
&lt;td&gt;✅ WebRTC P2P&lt;/td&gt;
&lt;td&gt;➖ Mostly relay-based&lt;/td&gt;
&lt;td&gt;➖ Can try direct paths&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fallback when direct fails&lt;/td&gt;
&lt;td&gt;✅ HTTPS relay / tunnel&lt;/td&gt;
&lt;td&gt;✅ Relay&lt;/td&gt;
&lt;td&gt;✅ Transit relay&lt;/td&gt;
&lt;td&gt;✅ Relay&lt;/td&gt;
&lt;td&gt;➖ Already cloud-hosted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relay impact on speed&lt;/td&gt;
&lt;td&gt;✅ Direct path avoids an extra hop; fallback only when needed&lt;/td&gt;
&lt;td&gt;➖ Often depends on relay location and bandwidth&lt;/td&gt;
&lt;td&gt;➖ Depends on the path&lt;/td&gt;
&lt;td&gt;✅ Good when direct works&lt;/td&gt;
&lt;td&gt;➖ Depends on the cloud service and region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E2EE&lt;/td&gt;
&lt;td&gt;✅ Supported&lt;/td&gt;
&lt;td&gt;✅ Supported&lt;/td&gt;
&lt;td&gt;✅ Supported&lt;/td&gt;
&lt;td&gt;✅ Transport encryption / protocol integrity&lt;/td&gt;
&lt;td&gt;➖ Depends on the service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recipient verification model&lt;/td&gt;
&lt;td&gt;✅ OTP / PubKey / Basic Auth, etc.&lt;/td&gt;
&lt;td&gt;🔑 The code is the pairing mechanism&lt;/td&gt;
&lt;td&gt;🔑 Wormhole code&lt;/td&gt;
&lt;td&gt;🎫 Ticket&lt;/td&gt;
&lt;td&gt;➖ Mostly link or account permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud upload / sender can go offline&lt;/td&gt;
&lt;td&gt;✅ Supported with upload mode&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Main workflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resume interrupted downloads&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;➖ Depends&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;➖ Depends on the service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Folder transfer&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;➖ Usually needs zipping first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distribution model&lt;/td&gt;
&lt;td&gt;✅ APE single file + native builds&lt;/td&gt;
&lt;td&gt;✅ Go binary per platform&lt;/td&gt;
&lt;td&gt;➖ Python package / OS package&lt;/td&gt;
&lt;td&gt;✅ Rust, installed per platform&lt;/td&gt;
&lt;td&gt;✅ Web service&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why WebRTC matters for speed and routing
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ffl&lt;/code&gt; tries WebRTC P2P by default. When a direct path works, data flows from the sender to the receiver without going through a relay. For large files, avoiding an extra hop can make a real difference.&lt;/p&gt;

&lt;p&gt;When P2P is not available, &lt;code&gt;ffl&lt;/code&gt; automatically falls back to HTTPS relay / tunnel mode. The transfer can still continue instead of failing because of NAT or firewall restrictions. If the recipient uses a browser or &lt;code&gt;curl&lt;/code&gt;, they use the HTTPS path. If the recipient also uses &lt;code&gt;ffl&lt;/code&gt;, it can try WebRTC first.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;croc&lt;/code&gt; leans more toward a relay-first design. That makes it very good at getting through NAT, but transfer speed can depend heavily on the relay’s location, bandwidth, and route. When you are moving large files across countries, corporate networks, or cloud regions, that difference can matter.&lt;/p&gt;

&lt;p&gt;Tools like sendme also have strong modern P2P capabilities. The main difference is still on the receiver side: the recipient needs the matching tool or ticket workflow, while &lt;code&gt;ffl&lt;/code&gt; can simply hand out a standard HTTPS download link.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where tools like croc shine
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;croc&lt;/code&gt; has a very clear strength: once both devices have it installed, you can pair them with a code and move a file quickly. For engineers, it is great for sending something from a laptop to a remote host, from a work machine to a home machine, or between two machines you control.&lt;/p&gt;

&lt;p&gt;Magic Wormhole has a similar appeal. Its code phrase is easy to communicate verbally, and its security model is mature. It works well when both sides are comfortable using the same CLI workflow.&lt;/p&gt;

&lt;p&gt;The sweet spot for these tools is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;both sides understand CLI
both sides can install the tool
the transfer is a one-time paired session
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that is your situation, &lt;code&gt;croc&lt;/code&gt; and Magic Wormhole are excellent tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where ffl fits better
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ffl&lt;/code&gt; is more useful when the situation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I need to send a file to someone who does not use CLI
I want the recipient to download from a browser
I want to share one link with multiple people
I want interrupted downloads to resume
I want curl / wget support for automation
I want both P2P speed and fallback reliability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In these cases, the HTTPS link is the key advantage.&lt;/p&gt;

&lt;p&gt;You do not need to check whether the recipient installed the right tool. You do not need to explain a code phrase flow. You send a link. The recipient can be on a browser, a phone, a server, or a CI job, and there is still a natural way to receive the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it differs from cloud-upload tools
&lt;/h2&gt;

&lt;p&gt;Services like transfer.sh, WeTransfer, and file.io also give you download links. Their usual flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;upload the file to a server
then let the recipient download it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is convenient, but it also means the file is stored on a service first. Large files have to finish uploading before the recipient can download them. Privacy, retention, and availability depend on the service provider.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ffl&lt;/code&gt; gives you more flexibility. It can try WebRTC P2P first, then fall back when needed. If the sender needs to go offline, upload mode can turn it into a temporary hosted delivery link.&lt;/p&gt;

&lt;p&gt;That covers two common workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;both sides online: prefer P2P
not online at the same time: use upload mode for temporary delivery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With E2EE enabled, the relay or temporary storage server cannot read the file contents.&lt;/p&gt;

&lt;h2&gt;
  
  
  APE: why portable single-file distribution matters
&lt;/h2&gt;

&lt;p&gt;Besides native builds, &lt;code&gt;ffl&lt;/code&gt; also provides an APE version. APE stands for &lt;a href="https://justine.lol/ape.html" rel="noopener noreferrer"&gt;Actually Portable Executable&lt;/a&gt;, from the Cosmopolitan ecosystem. In plain terms, it is a single executable format designed to run across multiple operating systems whenever possible.&lt;/p&gt;

&lt;p&gt;This is different from the usual Go or Rust single-binary story. Go and Rust tools often produce one executable per target platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;linux-amd64
linux-arm64
macos-arm64
windows-amd64.exe
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The APE experience is closer to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;download ffl.com
put it on a USB drive, home directory, shared folder, or toolbox
run it when needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is useful for DevOps work, emergency debugging, customer environments, containers, and machines where you do not have permission to install packages. You do not need a package manager, and you do not need to set up a runtime on the target machine.&lt;/p&gt;

&lt;p&gt;APE also makes &lt;code&gt;ffl&lt;/code&gt; unusually convenient as an embeddable transfer engine. The official &lt;a href="https://play.google.com/store/apps/details?id=com.fastfilelink.wrapper" rel="noopener noreferrer"&gt;Android app&lt;/a&gt; and the &lt;a href="https://github.com/nuwainfo/ffl-mcp" rel="noopener noreferrer"&gt;MCP Server&lt;/a&gt; both use the APE build as the core engine. The outer application handles the phone, AI agent, or server integration, while the same &lt;code&gt;ffl.com&lt;/code&gt; engine handles transfer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical DevOps scenarios where ffl feels different
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Pull a result out of a container
&lt;/h3&gt;

&lt;p&gt;You run a task inside a container and it produces a report, model, log bundle, or test artifact. The usual options may involve mounting a volume, setting up SCP, fixing permissions, or uploading to cloud storage first.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;ffl&lt;/code&gt;, you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffl /output/report.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints an HTTPS link. You can open it from your browser or download it from another machine with &lt;code&gt;curl&lt;/code&gt;. The more temporary the container environment is, the more useful this becomes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Stream a MySQL backup directly to someone or another machine
&lt;/h3&gt;

&lt;p&gt;Database backups often create huge &lt;code&gt;.sql&lt;/code&gt; files. You may not want to write the file locally first and then upload it somewhere.&lt;/p&gt;

&lt;p&gt;You can stream directly into &lt;code&gt;ffl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysqldump production_db | ffl - &lt;span class="nt"&gt;--name&lt;/span&gt; production_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recipient can download it from a browser, or receive it from another machine with &lt;code&gt;ffl&lt;/code&gt; or &lt;code&gt;curl&lt;/code&gt;. This is also useful when the output is part of a larger pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Share a large CI artifact with a reviewer
&lt;/h3&gt;

&lt;p&gt;Some build artifacts are too large or too temporary to put into GitHub Actions artifacts, S3, or an internal file service. Examples include debug builds, benchmark results, model weights, and compressed log bundles.&lt;/p&gt;

&lt;p&gt;From a CI runner or temporary build server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffl build/output/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then send the link to the reviewer. They open it in a browser. They do not need to know anything about the CI runner.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Move files from locked-down machines
&lt;/h3&gt;

&lt;p&gt;Production machines and customer environments often do not allow installing extra packages. They may not have Python, Node, or a convenient package manager.&lt;/p&gt;

&lt;p&gt;A portable &lt;code&gt;ffl.com&lt;/code&gt; binary is useful here. You can keep it in an internal tools directory, on a USB drive, on a jump box, or fetch it temporarily. Run it when needed, delete it when done.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Send technical files to non-engineers
&lt;/h3&gt;

&lt;p&gt;This happens more often than people expect. You may need to send logs, videos, datasets, model outputs, design assets, or diagnostic bundles to a PM, customer, QA person, or external partner.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;croc&lt;/code&gt;, the other person also needs &lt;code&gt;croc&lt;/code&gt;. With cloud drives, you may need to upload first, set permissions, and wait for sync.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;ffl&lt;/code&gt;, you send an HTTPS link. They download it from a browser. That is the simplest value proposition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one should you choose?
&lt;/h2&gt;

&lt;p&gt;If you are moving files between two machines you control, and both sides can install a CLI tool, &lt;code&gt;croc&lt;/code&gt;, Magic Wormhole, and sendme are all mature choices.&lt;/p&gt;

&lt;p&gt;If you are sending files to general users, clients, teammates, or multiple recipients, &lt;code&gt;ffl&lt;/code&gt; feels more natural because the receiver gets a normal download link.&lt;/p&gt;

&lt;p&gt;If your only goal is to upload a file to the cloud and let someone download it later, tools like transfer.sh and WeTransfer can do the job. They simply do not provide the same P2P-first path or the same portable CLI delivery-engine model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The shortest way to describe &lt;code&gt;ffl&lt;/code&gt; is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It turns CLI file transfer into HTTPS link delivery.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That gives it a clear distinction from paired one-to-one tools like &lt;code&gt;croc&lt;/code&gt; and Magic Wormhole. The receiver does not need to install anything. The same link can be downloaded by multiple people. The file can be received with a browser, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;wget&lt;/code&gt;, or &lt;code&gt;ffl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With WebRTC P2P, relay fallback, E2EE, upload mode, and portable APE distribution, &lt;code&gt;ffl&lt;/code&gt; works both as a daily CLI tool and as a practical file-delivery building block for automation.&lt;/p&gt;

&lt;p&gt;For a more detailed feature-by-feature breakdown, see the Wiki &lt;a href="https://github.com/nuwainfo/ffl/wiki/Comparison-with-Other-File-Transfer-Tools" rel="noopener noreferrer"&gt;detailed comparison&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Want to try it? &lt;a href="https://github.com/nuwainfo/ffl" rel="noopener noreferrer"&gt;Download the CLI version&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>cli</category>
      <category>networking</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Server Migration Without Extra Disk Space: Streaming Borg Backups with FastFileLink CLI</title>
      <dc:creator>Bear Huang</dc:creator>
      <pubDate>Tue, 05 May 2026 13:39:24 +0000</pubDate>
      <link>https://dev.to/bear0330/server-migration-without-extra-disk-space-streaming-borg-backups-with-fastfilelink-cli-3pc3</link>
      <guid>https://dev.to/bear0330/server-migration-without-extra-disk-space-streaming-borg-backups-with-fastfilelink-cli-3pc3</guid>
      <description>&lt;p&gt;Migrating a production app sounds like a simple copy job until the old server is nearly full.&lt;/p&gt;

&lt;p&gt;That is usually when migration becomes urgent. The machine is old, disk space is tight, the new server is only half-prepared, DNS cannot move yet, and the data set is large. If the migration flow still expects the source server to create one more giant tarball before anything can move, the process becomes much harder than it needs to be.&lt;/p&gt;

&lt;p&gt;This post is a field report from reworking our own migration flow. The original path was familiar: take a Borg backup, export it as a tar or tar.gz, copy it to the target with &lt;code&gt;scp&lt;/code&gt;, and restore it there.&lt;/p&gt;

&lt;p&gt;That works fine when disk space is plentiful. It is much less pleasant when the source machine is already under pressure, which is often the exact reason the migration is happening in the first place.&lt;/p&gt;

&lt;p&gt;So we moved the transfer layer to &lt;a href="https://fastfilelink.com" rel="noopener noreferrer"&gt;FastFileLink CLI&lt;/a&gt; and turned the whole operation into a streaming path. The source no longer needs to stage a tarball. The target does not need to download one either. Data flows from the old machine to the new one and lands where it needs to be restored.&lt;/p&gt;

&lt;p&gt;This pattern is useful well beyond our own deploy system. If you need to move large backups, restore production data into a development VM, or transfer a one-off archive without prearranged SSH trust, it is a very practical approach.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Our Deployment Model
&lt;/h2&gt;

&lt;p&gt;Internally, we run a lightweight deployment system with a PaaS-like shape. The main entry point is &lt;code&gt;Deploy.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It handles install, backup, restore, migrate, update, health checks, and the surrounding operational work. Shared behavior lives in the deployment layer, while each app keeps its own app-specific scripts and settings.&lt;/p&gt;

&lt;p&gt;We do not use Kubernetes for this class of workload. The reason is simple: for our current environment, the total complexity is not a good trade. Many of our services are small to medium production apps. We care more about clear single-host operations, readable scripts, predictable recovery, and being able to understand the entire lifecycle from the app directory.&lt;/p&gt;

&lt;p&gt;In that context, a structured deployment interface is valuable. Full cluster orchestration would be more machinery than we actually need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why App Working Folders Matter
&lt;/h2&gt;

&lt;p&gt;A key design choice in this system is that every app has its own working folder.&lt;/p&gt;

&lt;p&gt;That folder contains runtime configuration, container definitions, service scripts, app-level maintenance tools, and the rules for restoring persistent volumes. The database is part of the same operational contract through dumps, backup hooks, or related tooling.&lt;/p&gt;

&lt;p&gt;In practice, if we can recreate the following pieces on another machine, the app can usually come up there before any DNS cutover:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Container definition&lt;/td&gt;
&lt;td&gt;Recreates the service process and runtime image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volumes&lt;/td&gt;
&lt;td&gt;Preserves uploads, user data, and other persistent state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;Preserves structured application data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration&lt;/td&gt;
&lt;td&gt;Recreates domains, ports, paths, secrets, and environment variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App maintenance tools&lt;/td&gt;
&lt;td&gt;Gives the new host the same install, backup, restore, and migrate interface&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is why each app has its own &lt;code&gt;bin&lt;/code&gt; tools such as &lt;code&gt;backup&lt;/code&gt;, &lt;code&gt;install&lt;/code&gt;, &lt;code&gt;restore&lt;/code&gt;, and &lt;code&gt;migrate&lt;/code&gt;. The global deployment layer invokes them, but the app still owns the app-specific details.&lt;/p&gt;

&lt;p&gt;This design is intentionally plain. In production operations, plain is often a strength.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Old Migration Flow Looked Like
&lt;/h2&gt;

&lt;p&gt;The old migration flow looked roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source server
  -&amp;gt; run Borg backup
  -&amp;gt; export volumes from Borg
  -&amp;gt; create a tar stream or tar file on disk
  -&amp;gt; copy it to the target with scp

target server
  -&amp;gt; receive the archive
  -&amp;gt; extract it
  -&amp;gt; restore database and config
  -&amp;gt; start the app
  -&amp;gt; verify before cutover
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is an easy flow to understand. Borg is a solid backup tool. &lt;code&gt;scp&lt;/code&gt; is familiar to almost every operator. Tarballs are easy to inspect.&lt;/p&gt;

&lt;p&gt;The real problem is the disk-usage pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pain Point: the Source Server Is Already Short on Space
&lt;/h2&gt;

&lt;p&gt;Suppose an app has 80 GB of volume data.&lt;/p&gt;

&lt;p&gt;The old path can force the source server to carry all of this at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;existing application data
+ Borg repository or backup cache
+ exported tar archive
+ optional compressed archive
+ partial transfer file
+ logs and temporary files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That requirement clashes with the reason migrations happen in the first place. The machines that need to be moved tend to be the ones that are already too full, too old, or too awkward to keep extending. Asking that same machine to produce one more large copy of the data is the worst possible requirement.&lt;/p&gt;

&lt;p&gt;The more sensible approach is to let the backup output enter the transfer pipeline directly, without landing on the source disk first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Could rsync Solve This?
&lt;/h2&gt;

&lt;p&gt;If the source data already exists as a directory tree and SSH connectivity between source and target is already in place, &lt;code&gt;rsync&lt;/code&gt; remains excellent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-aHAX&lt;/span&gt; &lt;span class="nt"&gt;--numeric-ids&lt;/span&gt; &lt;span class="nt"&gt;--partial&lt;/span&gt; &lt;span class="nt"&gt;--partial-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.rsync-partial &lt;span class="se"&gt;\&lt;/span&gt;
  /srv/apps/myapp/volumes/ user@target:/srv/apps/myapp/volumes/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It resumes, preserves metadata, and is very good at synchronizing real directories. If migration simply meant "copy the live volumes and start the app somewhere else," &lt;code&gt;rsync&lt;/code&gt; would be a very reasonable choice, and in many environments it may well be faster than a relay-capable sharing tool.&lt;/p&gt;

&lt;p&gt;Our situation was slightly different. We already take a backup before migration. Once that backup exists, using it as the data source is natural. The new Borg archive is a clean point-in-time snapshot, and &lt;code&gt;borg export-tar&lt;/code&gt; gives us a ready-made stdout stream that can be fed straight into a transfer pipeline.&lt;/p&gt;

&lt;p&gt;That is an important distinction. Borg is not the reason migration exists here. It is simply the backup mechanism we already use and trust. After the backup finishes, it becomes a convenient source for the migration stream. Whether to transfer the full backup history to the new machine is a separate decision. In many environments it is optional because full-machine backups already exist elsewhere.&lt;/p&gt;

&lt;p&gt;The other factor is target flexibility. Sometimes the target is a clean VM or a temporary test environment, and we do not want to deal with SSH keys, &lt;code&gt;sshpass&lt;/code&gt;, or extra server-to-server trust setup before moving the first byte.&lt;/p&gt;

&lt;p&gt;Under those conditions, a stream-oriented transfer tool is a better fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why FastFileLink CLI Fits So Well
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/nuwainfo/ffl" rel="noopener noreferrer"&gt;FastFileLink CLI&lt;/a&gt; can transfer files, folders, and stdin. That is the capability that changed the design.&lt;/p&gt;

&lt;p&gt;Instead of exporting to a tarball and then copying that tarball, we can keep the data moving the entire time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;borg export-tar -&amp;gt; stdout -&amp;gt; FastFileLink CLI -&amp;gt; stdout on target -&amp;gt; tar extract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source does not need to stage a tarball. The target does not need to download one either.&lt;/p&gt;

&lt;p&gt;The sender side looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;borg export-tar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BORG_REPO&lt;/span&gt;&lt;span class="s2"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$ARCHIVE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; - &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FFL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; - &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;&lt;span class="s2"&gt;-volumes.tar"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--e2ee&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--stdin-cache&lt;/span&gt; off &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--max-downloads&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--pickup-code&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PICKUP_CODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The receiver side sends the stream straight into &lt;code&gt;tar&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FFL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; download &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LINK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pickup-code&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PICKUP_CODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--e2ee&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stdout&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;tar &lt;/span&gt;xvf - &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RESTORE_ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives us exactly the shape we wanted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source server: no exported archive on disk
target server: no downloaded archive on disk
transfer path: data streams directly into the target layout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command shape is intentionally plain tar. &lt;code&gt;borg export-tar&lt;/code&gt; produces a tar stream here, so the transfer is named &lt;code&gt;.tar&lt;/code&gt;, and the receiver uses &lt;code&gt;tar xvf -&lt;/code&gt;. If you want gzip in the middle, it is better to write it explicitly, for example &lt;code&gt;borg export-tar ... - | gzip -c | ffl ...&lt;/code&gt;, and then extract with &lt;code&gt;tar xzvf -&lt;/code&gt; on the target.&lt;/p&gt;

&lt;p&gt;We kept gzip out of the main example on purpose. In this migration, the primary problem was extra disk usage, not compression ratio, and a plain tar stream keeps CPU cost and troubleshooting overhead lower on both ends.&lt;/p&gt;

&lt;p&gt;For low-space migration, that is the real win.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;--stdin-cache&lt;/code&gt;, &lt;code&gt;--stdout&lt;/code&gt;, and &lt;code&gt;--pickup-code&lt;/code&gt; Are Doing
&lt;/h2&gt;

&lt;p&gt;If you just paste the commands and move on, a lot of readers will not immediately understand why these flags matter.&lt;/p&gt;

&lt;p&gt;Start with &lt;code&gt;--pickup-code&lt;/code&gt;. It is part of the pairing flow. The sender produces a link, but the receiver still needs the pickup code before it can actually claim the transfer. If you have used &lt;code&gt;croc&lt;/code&gt;, this style of short-code trust model will feel familiar. In a migration script, it is convenient because we can hand the link and the code to the target process without exposing an open download endpoint. Once &lt;code&gt;--pickup-code&lt;/code&gt; is present, FastFileLink CLI already knows that pickup mode is being used, so there is no need to also spell out &lt;code&gt;--recipient-auth pickup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;FastFileLink CLI is more flexible than that single mode suggests. Besides &lt;code&gt;pickup&lt;/code&gt;, it also supports receiver-verification modes such as &lt;code&gt;pubkey&lt;/code&gt;, &lt;code&gt;pubkey+pickup&lt;/code&gt;, and &lt;code&gt;email&lt;/code&gt;, plus traditional HTTP Basic Authentication through &lt;code&gt;--auth-user&lt;/code&gt; and &lt;code&gt;--auth-password&lt;/code&gt;. We chose &lt;code&gt;pickup&lt;/code&gt; here because it is a very smooth fit for a one-off scripted migration.&lt;/p&gt;

&lt;p&gt;Next is &lt;code&gt;--stdin-cache off&lt;/code&gt;. FastFileLink CLI's default mental model is general-purpose file sharing. Files might be downloaded more than once, or by multiple receivers, so caching is useful there. A migration stream is the opposite. We know there will be a single receiver consuming the data once. Keeping a stdin cache in that scenario only spends extra source-side disk space. Turning the cache off makes the sender behave much more like a real one-shot stream producer.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--e2ee&lt;/code&gt; is worth enabling too. WebRTC already runs over secure transport, but migration jobs can fall back to relay or tunnel paths. With end-to-end encryption enabled, even those middle hops only see encrypted chunks. For backup material and production data, that extra protection is well worth having.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;--stdout&lt;/code&gt; is what makes the target side clean. The receiver writes the bytes to stdout, and &lt;code&gt;tar&lt;/code&gt; unpacks them directly into place. There is no downloaded tarball to clean up afterward.&lt;/p&gt;

&lt;p&gt;Put together, the data path becomes very straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source app backup snapshot
  -&amp;gt; Borg export stream
  -&amp;gt; FastFileLink CLI sender
  -&amp;gt; FastFileLink CLI receiver
  -&amp;gt; tar extracts directly into the target app folder
  -&amp;gt; volumes land in place
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How This Fits Back into Deploy.py
&lt;/h2&gt;

&lt;p&gt;We did not build a completely separate migration tool.&lt;/p&gt;

&lt;p&gt;The existing &lt;code&gt;Deploy.py migrate&lt;/code&gt; flow already knew how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prepare the source and target app layout&lt;/li&gt;
&lt;li&gt;invoke the app's own migrate and backup scripts&lt;/li&gt;
&lt;li&gt;export volumes&lt;/li&gt;
&lt;li&gt;dump and restore the database&lt;/li&gt;
&lt;li&gt;restore configuration&lt;/li&gt;
&lt;li&gt;start the target app and verify it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the job was to swap out the transfer mechanics while leaving the rest of the workflow intact.&lt;/p&gt;

&lt;p&gt;The resulting shape now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deploy.py migrate
  -&amp;gt; app bin/migrate
  -&amp;gt; ExportVolumes from Borg
  -&amp;gt; source streams into FastFileLink CLI
  -&amp;gt; target receives from FastFileLink CLI
  -&amp;gt; volumes extract directly into the target app folder
  -&amp;gt; database and privileges are restored
  -&amp;gt; backup history can also be transferred when desired
  -&amp;gt; target app is installed and started
  -&amp;gt; verification happens before cutover
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also keeps the old file-based path available. If we want to preserve an artifact, debug restore behavior in isolation, or simply keep the older flow for a roomy machine, that option still exists. Streaming mode is there to solve the low-space migration problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What FastFileLink CLI Brings to This Case
&lt;/h2&gt;

&lt;p&gt;FastFileLink CLI matches this scenario surprisingly well because it brings several useful properties at once:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Why it helps migration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Single-file APE binary&lt;/td&gt;
&lt;td&gt;Download when needed, delete after use, no permanent install&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebRTC direct transfer&lt;/td&gt;
&lt;td&gt;Tries peer-to-peer paths when the network allows it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relay fallback&lt;/td&gt;
&lt;td&gt;Still completes when direct connectivity fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;End-to-end encryption&lt;/td&gt;
&lt;td&gt;Relay infrastructure cannot see plaintext data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pickup-code verification&lt;/td&gt;
&lt;td&gt;Scripts can pair sender and receiver safely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--e2ee&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relay or tunnel hops still cannot inspect contents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stdin / stdout support&lt;/td&gt;
&lt;td&gt;Makes true no-staging transfer possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--hook&lt;/code&gt; event output&lt;/td&gt;
&lt;td&gt;Integrates cleanly into existing automation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For an old production host, the single-file binary is a bigger advantage than it first sounds. We do not need to permanently install another service just to move away from that machine. Download &lt;code&gt;ffl.com&lt;/code&gt;, run the migration, clean it up, and move on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--hook&lt;/code&gt; is also worth calling out on its own. FastFileLink CLI has its own &lt;a href="https://github.com/nuwainfo/ffl/wiki/Embedded-Mode-&amp;amp;-Event-Hooks" rel="noopener noreferrer"&gt;embedded mode&lt;/a&gt;, which can emit structured events for integration. That saved us a lot of glue code, because we no longer had to parse human-oriented console output just to understand what the transfer was doing. That kind of integration surface is unusual in this category of tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing FastFileLink CLI with scp, rsync, croc, and Magic Wormhole
&lt;/h2&gt;

&lt;p&gt;The real question with transfer tools is not which one has the flashiest feature list. It is whether the tool's assumptions match the environment you are actually operating in.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best fit&lt;/th&gt;
&lt;th&gt;Limitation in this scenario&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Straightforward file copy over SSH&lt;/td&gt;
&lt;td&gt;Needs a prebuilt artifact and SSH access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Synchronizing live directory trees&lt;/td&gt;
&lt;td&gt;Not a natural fit for Borg stdout streams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;croc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secure CLI transfer with pipe support&lt;/td&gt;
&lt;td&gt;A good fallback option, but large transfers often end up traversing relay infrastructure, which is not very attractive for multi-hundred-megabyte or multi-gigabyte migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Magic Wormhole&lt;/td&gt;
&lt;td&gt;Human-friendly one-time transfer&lt;/td&gt;
&lt;td&gt;Better for ad hoc exchange than unattended migration pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FastFileLink CLI&lt;/td&gt;
&lt;td&gt;File, folder, stdin, and stdout streaming&lt;/td&gt;
&lt;td&gt;Needs proper orchestration for logs, cleanup, and timeout handling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the data is already a plain directory tree and SSH trust is stable, &lt;code&gt;rsync&lt;/code&gt; remains very strong.&lt;/p&gt;

&lt;p&gt;But when the data source is a backup stream and the target might be a fresh VM, FastFileLink CLI feels better aligned with the real constraints. It asks less from the environment while still giving us encryption, verification, direct-transfer attempts, and relay fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Lessons from Implementation
&lt;/h2&gt;

&lt;p&gt;The most valuable lessons did not come from the happy path. They came from the first few attempts, when large transfers could stall and the surrounding automation did not have enough visibility into what was happening.&lt;/p&gt;

&lt;p&gt;Our earliest version was the obvious one: start FastFileLink CLI, parse its normal output, grab the link, and let the rest of the wrapper script take it from there. That was fine for a proof of concept. It was not good enough for a real migration command.&lt;/p&gt;

&lt;p&gt;Large data transfers make every observability problem feel bigger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the sender actually begin reading stdin?&lt;/li&gt;
&lt;li&gt;Did the receiver really connect?&lt;/li&gt;
&lt;li&gt;Are we using a direct path or a fallback path?&lt;/li&gt;
&lt;li&gt;If both processes are still alive, is progress real or are we just stuck?&lt;/li&gt;
&lt;li&gt;Did the producer fail first, or did the transfer tool fail first?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Orchestration Matters More Than the Pipe
&lt;/h2&gt;

&lt;p&gt;Streaming migration is not finished just because we connected a producer and a consumer with one more pipe.&lt;/p&gt;

&lt;p&gt;When a file-based transfer fails, there is usually still a partial file sitting on disk. When a stream stalls, the system has to tell us where it stalled. The difference between a neat demo and a production migration command is usually not whether the tool supports stdin. It is whether the orchestration around that tool was designed with observability, cleanup, and retry boundaries in mind.&lt;/p&gt;

&lt;p&gt;In practice, we found that a few things have to be designed explicitly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;temporary binary&lt;/td&gt;
&lt;td&gt;Download &lt;code&gt;ffl.com&lt;/code&gt; into a temporary directory and remove it afterward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;logging&lt;/td&gt;
&lt;td&gt;Be able to switch to DEBUG or point to a logging config when a transfer stalls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;receiver readiness&lt;/td&gt;
&lt;td&gt;Confirm that the target receiver has actually started before letting the workflow continue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;progress&lt;/td&gt;
&lt;td&gt;Watch bytes transferred or extracted, not just whether the process still exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cleanup&lt;/td&gt;
&lt;td&gt;Remove orphan sender / receiver processes and temporary files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;retry&lt;/td&gt;
&lt;td&gt;Retry at the transfer boundary instead of trying to resume from the middle of a half-extracted tar stream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secrets&lt;/td&gt;
&lt;td&gt;Keep auth passwords and pickup codes out of ordinary logs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We learned this very directly during testing. If the target receiver never really comes up, the source side can still look busy for a while. It may appear to be waiting or doing useful work, even though the migration is already stuck.&lt;/p&gt;

&lt;p&gt;So a robust migration command should at least be able to confirm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source process is alive
target process is alive
target output is growing
logs show an accepted transfer path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For long-running transfers, "the process still exists" is not progress. Progress has to be observable.&lt;/p&gt;

&lt;p&gt;Once we switched to FastFileLink CLI's &lt;code&gt;--hook&lt;/code&gt; support, things got much smoother. Share links, progress, receiver state, completion, and failure events could all be tracked as structured data. That was the point where we finally stopped treating human-readable output as an API.&lt;/p&gt;

&lt;p&gt;That shift matters a lot. Migration automation needs more than bytes moving over the wire. It needs state that can be trusted.&lt;/p&gt;

&lt;p&gt;On top of that, we still fixed a few ordinary deploy bugs, such as target path quoting and the cleanup order for old volumes. But those became straightforward engineering issues once the transfer state itself was observable.&lt;/p&gt;

&lt;p&gt;After that, the migration path was able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stream volumes from Borg through FastFileLink CLI&lt;/li&gt;
&lt;li&gt;restore database dumps on the target&lt;/li&gt;
&lt;li&gt;restore database privileges&lt;/li&gt;
&lt;li&gt;optionally bring over the Borg backup repository&lt;/li&gt;
&lt;li&gt;pull the app image&lt;/li&gt;
&lt;li&gt;start the target container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The remaining issue in our test was no longer in the streaming layer. It was an Nginx / SSL cleanup detail on the target side. That was actually reassuring: the hardest part of the move had become ordinary deployment finish-up.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Can Test the Migration Before DNS Moves
&lt;/h2&gt;

&lt;p&gt;A good migration flow should let us validate the new machine before the real cutover happens.&lt;/p&gt;

&lt;p&gt;That fits nicely with our app-folder design. Because the container definition, volumes, database, config, and app maintenance tools already belong to the same working-folder contract, we can restore the app onto the new machine, start it with a test port or a temporary hostname, and confirm that it behaves correctly before touching DNS or the load balancer.&lt;/p&gt;

&lt;p&gt;In practice, the checklist is fairly simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Restore app files, volumes, database, and config on the target.
2. Start the target containers or services.
3. Run health checks.
4. Verify static files and uploaded files.
5. Verify database-backed pages and the login flow.
6. Check logs for path, permission, or environment-variable issues.
7. Only then schedule the DNS or load-balancer cutover.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is one of the reasons migration becomes less scary. The old production server keeps serving traffic while the new machine proves, right next to it, that it is ready to take over.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Pattern Is Useful Beyond One App
&lt;/h2&gt;

&lt;p&gt;Even though this work grew out of one migration project, the pattern generalizes very well.&lt;/p&gt;

&lt;p&gt;Any time the data source can write to stdout, the same transfer model becomes available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;borg export-tar repo::archive -
pg_dump mydb
mysqldump mydb
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-cf&lt;/span&gt; - /large/folder
zfs send pool/dataset@snapshot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pipe that into FastFileLink CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;producer | ffl - &lt;span class="nt"&gt;--stdin-cache&lt;/span&gt; off &lt;span class="nt"&gt;--max-downloads&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then let the target feed it directly into the real consumer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffl download &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LINK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--stdout&lt;/span&gt; | consumer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a good fit for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migrating old or nearly full servers&lt;/li&gt;
&lt;li&gt;restoring production backups into development VMs&lt;/li&gt;
&lt;li&gt;disaster recovery transfers&lt;/li&gt;
&lt;li&gt;moving large user-upload archives&lt;/li&gt;
&lt;li&gt;transferring database dumps without leaving dump files behind&lt;/li&gt;
&lt;li&gt;temporary environments where prearranged SSH trust is inconvenient&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;The most important part of this migration change was not a new compression trick or a more elaborate archive format.&lt;/p&gt;

&lt;p&gt;It was rewriting the migration contract.&lt;/p&gt;

&lt;p&gt;The old contract required the source server to prepare one more large copy of the data before it could move away.&lt;/p&gt;

&lt;p&gt;The new version is much simpler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read the backup stream
transfer it immediately
restore it directly on the target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--stdin-cache off&lt;/code&gt; and &lt;code&gt;--stdout&lt;/code&gt; fit that model extremely well. For teams dealing with server migration, backup transfer, low-disk hosts, or temporary restore environments, this turns out to be a practical, automation-friendly, and easy-to-reason-about approach.&lt;/p&gt;

&lt;p&gt;When the old server is full and migration has become urgent, that is exactly the behavior you want from the toolchain.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>webdev</category>
      <category>automation</category>
    </item>
    <item>
      <title>Is AI Making Programming Harder? Why "Test Only, Zero Code Review" is an Absolute Disaster</title>
      <dc:creator>Bear Huang</dc:creator>
      <pubDate>Sat, 21 Feb 2026 03:03:51 +0000</pubDate>
      <link>https://dev.to/bear0330/is-ai-making-programming-harder-why-test-only-zero-code-review-is-an-absolute-disaster-46nf</link>
      <guid>https://dev.to/bear0330/is-ai-making-programming-harder-why-test-only-zero-code-review-is-an-absolute-disaster-46nf</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjn6fprgfjs8u2z29ntt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjn6fprgfjs8u2z29ntt.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recently, an article titled &lt;a href="https://siddhantkhare.com/writing/ai-fatigue-is-real" rel="noopener noreferrer"&gt;"AI Fatigue is Real"&lt;/a&gt; struck a massive chord, perfectly echoing what many developers are feeling right now: &lt;strong&gt;programming in the AI era has actually become more exhausting.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because we’ve transitioned from being Creators (Builders) to Reviewers. AI generates code so fast and inconsistently that the burden of reviewing has become overwhelmingly heavy: first, it's impossible to enter a flow state; second, you are constantly context-switching; third, this kind of high-density micro-reviewing is incredibly draining and quickly pushes you into "decision fatigue."&lt;/p&gt;

&lt;p&gt;Recently, while promoting my open-source project, the &lt;a href="https://github.com/nuwainfo/ffl" rel="noopener noreferrer"&gt;&lt;strong&gt;fastfilelink CLI (ffl)&lt;/strong&gt;&lt;/a&gt;, across various communities, I’ve been lurking and observing mainstream developer discussions. What I saw was endless anxiety, boundless hype, and a flood of "AI will replace developers" methodologies, all accompanied by massive amounts of poorly crafted code.&lt;/p&gt;

&lt;p&gt;Some of it makes sense, but a huge portion of it is, frankly speaking, utter bullshit.&lt;/p&gt;

&lt;p&gt;To combat this fatigue, a new, incredibly sweet-sounding proposition has emerged in the community (especially driven by the current OpenClaw-dominated faction):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Don’t review code; review output / tests passed."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some people even use compilers as an analogy, arguing that we don't review the assembly generated by a compiler today either. Personally, I think this argument is complete dogshit. The reason is simple:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. LLMs are not Compilers; they lack "Determinism"
&lt;/h2&gt;

&lt;p&gt;A compiler uses strict syntax for deterministic translation, but an LLM is a probabilistic model. When you ask an LLM to generate test cases, sometimes the tests themselves are fake or of terrible quality. Even if all the tests show green, it doesn't mean the system is fine. You ultimately &lt;em&gt;have&lt;/em&gt; to review these test cases, so the premise of "not reviewing at all" is fundamentally unreliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Lack of Global Vision leads to the collapse of the Single Source of Truth
&lt;/h2&gt;

&lt;p&gt;Constrained by the extreme limits of context windows and RAG capabilities, LLMs can only see fragments of a project; they cannot comprehend the system as a whole. This results in generated code packed with extreme redundancy, completely violating the DRY (Don't Repeat Yourself) principle. Once requirements change, it's incredibly prone to producing a massive amount of inconsistent bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Context Window Limit is a short-term unsolvable fatal flaw
&lt;/h2&gt;

&lt;p&gt;Stop fantasizing that "it'll be fine once the models get stronger." Bound by the physical limits of training data and attention mechanisms, context windows will always have a ceiling (not to mention that no matter how big the context window gets, projects will only get bigger). Therefore, the architectural collapse mentioned in Point 2 is practically unsolvable in the foreseeable future.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Trapped in a "Chicken-and-Egg" infinite loop
&lt;/h2&gt;

&lt;p&gt;To prevent those weird, unpatched, and inconsistent edge-case bugs from Point 2, your Test Coverage must approach 100%, and the tests must be designed with extreme rigor. This brings us right back to Point 1: Test Cases are themselves Code, heavily copy/pasted by the LLM.&lt;/p&gt;

&lt;p&gt;If you want to save yourself the effort of reviewing Production Code, you must spend equal (or even more) effort reviewing a massive pile of Test Code. You are merely trading one hell for another.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cruel Reality: This is a game of "Compute Capitalism"
&lt;/h2&gt;

&lt;p&gt;Therefore, the methodology of only caring if test cases pass while ignoring the code has fundamental flaws. It's more of an AI hype—it sounds awesome, but in practice, it’s riddled with issues. You're just swapping one problem for another.&lt;/p&gt;

&lt;p&gt;Of course, you might argue: "But it seems to work! As long as we only care about tests/outputs, if we give the LLM enough time and tokens to fix it, it can eventually pass the tests."&lt;/p&gt;

&lt;p&gt;Oh, yeah, sure! Because as the project grows larger and gets filled with more and more repetitive logic created by the LLM, you can indeed just throw money at it and let it slowly fix things. But the key points are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not reviewing is impossible:&lt;/strong&gt; You’ve just shifted the target of your review from Production Code to Test Cases. That is still Code, and it still drains your brainpower.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance costs compound exponentially:&lt;/strong&gt; Once technical debt stacks up, fixing even a minor bug will cost increasingly more tokens and time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People are lazy. Who wouldn't want to dump all their work onto AI? (Especially for the vast majority of developers who don't even enjoy coding that much—to them, it's just a job). So, this narrative that "AI can do everything for you" is guaranteed to excite people, while simultaneously peddling anxiety.&lt;/p&gt;

&lt;p&gt;But who is the ultimate beneficiary? &lt;strong&gt;It's the AI development tool vendors.&lt;/strong&gt; The more bloated the code is, and the more times you need to debug, the more money they make. The cruel truth they don’t want you to know is that the oldest software engineering adage still holds true: &lt;strong&gt;"No Silver Bullet."&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>rant</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
