<?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: Joichiro Mitaka</title>
    <description>The latest articles on DEV Community by Joichiro Mitaka (@coldstorage).</description>
    <link>https://dev.to/coldstorage</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3991890%2F8d80d521-aaef-4748-9485-eabc2f04b9ed.png</url>
      <title>DEV Community: Joichiro Mitaka</title>
      <link>https://dev.to/coldstorage</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coldstorage"/>
    <language>en</language>
    <item>
      <title>Why Is Only 40% of Your Data Compressed? The Tarball Trap and How to Escape It</title>
      <dc:creator>Joichiro Mitaka</dc:creator>
      <pubDate>Mon, 06 Jul 2026 07:06:25 +0000</pubDate>
      <link>https://dev.to/coldstorage/why-is-only-40-of-your-data-compressed-the-tarball-trap-and-how-to-escape-it-2004</link>
      <guid>https://dev.to/coldstorage/why-is-only-40-of-your-data-compressed-the-tarball-trap-and-how-to-escape-it-2004</guid>
      <description>&lt;h2&gt;
  
  
  The Uncompressed Elephant in the Room
&lt;/h2&gt;

&lt;p&gt;Open a shell on your primary storage array. Run &lt;code&gt;du -sh&lt;/code&gt; on the directories nobody has touched since 2022. Now look at your cloud bill. There's a good chance you're paying full price to store data that could be sitting in less than half the space.&lt;/p&gt;

&lt;p&gt;Industry analysts have been saying the same thing for years: somewhere around 80% of enterprise data is unstructured — logs, CSVs, old project exports, documents, media, backups of backups. And unstructured data is exactly the kind of data that modern compression algorithms are good at. Zstd routinely gets 2:1 to 3:1 on typical unstructured payloads, which means 50-66% of that storage footprint doesn't need to exist.&lt;/p&gt;

&lt;p&gt;So here's the question worth sitting with: if the algorithms are this good, and the financial case is this obvious, why does so much cold data sit completely uncompressed on expensive primary storage?&lt;/p&gt;

&lt;p&gt;The answer isn't ignorance. It's fear — a very rational fear, rooted in how compression tools actually behave once you use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Don't Compress: The Tarball Trap
&lt;/h2&gt;

&lt;p&gt;Everyone has run &lt;code&gt;tar czf archive.tar.gz project/&lt;/code&gt; at some point, watched a directory of ten thousand files collapse into a single blob, and felt the small dread of knowing that directory is now effectively gone.&lt;/p&gt;

&lt;p&gt;This is the core usability problem with traditional compression:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loss of visibility.&lt;/strong&gt; Once files are inside a tarball or zip, they vanish from the filesystem's view. No more &lt;code&gt;find&lt;/code&gt;, no more &lt;code&gt;grep -r&lt;/code&gt;, no more casually browsing a folder in your file manager. The files still exist, technically, but they're locked behind an opaque container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "I might need that" price.&lt;/strong&gt; Someone eventually needs one 5MB config file out of that archive. To get it, they have to pull down and decompress the entire 100GB tarball — CPU time, disk I/O, and a coffee break, just to retrieve a file a fraction of a percent the size of the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Faced with that trade-off, most teams make the rational choice: leave it uncompressed. Storage is "cheap enough," and a working filesystem beats a compressed black box you're afraid to touch. We don't skip compression because we don't understand its value — we skip it because traditional tooling makes accessibility and compression mutually exclusive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Financial Cost of That Fear
&lt;/h2&gt;

&lt;p&gt;Choosing not to compress isn't a one-time decision that costs you some disk space. It's a decision that gets multiplied across your entire infrastructure stack, every day, forever.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backups.&lt;/strong&gt; Every backup job now moves and stores the full uncompressed volume — 100TB instead of 40TB, on every cycle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replication.&lt;/strong&gt; Cloud egress fees and inter-site bandwidth are spent transmitting bytes that were mostly redundant text or empty padding to begin with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Power and cooling.&lt;/strong&gt; Uncompressed data means more spinning drives, drawing wattage around the clock, to hold data nobody has read in three years.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these costs show up as a single line item labeled "we didn't compress our archive." They show up quietly, spread across every budget line that touches storage — which is exactly why they're so easy to ignore and so expensive to sustain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Compression That Doesn't Change the User Experience
&lt;/h2&gt;

&lt;p&gt;The real fix isn't a better zip format. It's removing the trade-off entirely: what if data could be compressed at rest, but still look and behave like a normal, uncompressed file to everything above the storage layer?&lt;/p&gt;

&lt;p&gt;That's the specific problem &lt;a href="https://huskhoard.com" rel="noopener noreferrer"&gt;HuskHoard&lt;/a&gt; is built around. It's an open-source (&lt;a href="https://github.com/HuskHoard/HuskHoard/blob/main/LICENSE" rel="noopener noreferrer"&gt;AGPL v3&lt;/a&gt;), Rust-based tiering engine for Linux that automatically moves cold data off expensive NVMe/SSD storage onto disk, cloud buckets, or physical &lt;a href="https://huskhoard.com/blog-post-lto.html" rel="noopener noreferrer"&gt;LTO tape&lt;/a&gt; — while leaving a normal-looking file behind in your directory tree.&lt;/p&gt;

&lt;p&gt;The mechanism that makes this work is worth calling out, because it's not FUSE. FUSE-based transparent filesystems exist, but they route every single read through a userspace filesystem driver, which adds latency and a layer you now depend on for correctness. HuskHoard instead uses the Linux &lt;a href="https://huskhoard.com/blog-post-fa.html" rel="noopener noreferrer"&gt;&lt;code&gt;fanotify&lt;/code&gt;&lt;/a&gt; kernel API. When a process opens a file that's been archived, the kernel itself pauses that process, HuskHoard's daemon recalls the data, and the process resumes — with zero awareness that anything happened. &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, and your file manager all keep working normally, because as far as the filesystem is concerned, the file is still there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Layer: How the Catalog and Frames Actually Work
&lt;/h2&gt;

&lt;p&gt;This is the part worth understanding if you're evaluating whether this approach holds up under real workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Catalog fixes visibility.&lt;/strong&gt; Instead of hiding archived files inside a monolithic container, HuskHoard logs every file's metadata — path, version history, byte offset on physical media, &lt;a href="https://huskhoard.com/blog-post-blake.html" rel="noopener noreferrer"&gt;BLAKE3&lt;/a&gt; hash — into a SQLite database it calls the &lt;a href="https://huskhoard.com/blog-post-cat.html" rel="noopener noreferrer"&gt;Catalog&lt;/a&gt;. The filesystem keeps a lightweight stub in place of the original file, so directory listings, &lt;code&gt;find&lt;/code&gt;, and search all keep working exactly as before. You never lose sight of what you have or where it lives; the Catalog is queryable in milliseconds even across a multi-petabyte archive, without waking up a single tape drive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Independent &lt;a href="https://huskhoard.com/blog-post-zstd.html" rel="noopener noreferrer"&gt;Zstd frames&lt;/a&gt; fix the "give me 5MB out of 100GB" problem.&lt;/strong&gt; A standard Zstd or gzip stream builds a single continuous compression window across the whole file — to read byte 90,000,000 you generally have to decompress everything before it. HuskHoard avoids this by packing archived data into independent 16MB Zstd frames when writing to cloud storage via &lt;code&gt;rclone&lt;/code&gt;. Each frame is a self-contained compression boundary, so decompressing frame 400 doesn't require touching frames 1 through 399. This is also what keeps cloud costs down on the write side — batching into 16MB units minimizes the number of PUT requests, which matters when providers bill per-request as well as per-byte.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://huskhoard.com/blog-post-lto.tag" rel="noopener noreferrer"&gt;TLV headers&lt;/a&gt; carry the metadata that makes recovery possible without a working database.&lt;/strong&gt; Every file HuskHoard archives is preceded by a 4,096-byte object header. The first 136 bytes hold the essentials — UUID, POSIX mode, compressed size, BLAKE3 hash. The remaining ~3,960 bytes are packed using Type-Length-Value (TLV) encoding, which stores POSIX extended attributes (xattrs) directly alongside the payload. That detail matters more than it sounds: if a parser encounters a TLV type it doesn't recognize, it just reads the length and skips it, which is what makes the format tolerant of future changes. It also means the archive is self-describing — if the Catalog database is ever lost, HuskHoard can rebuild it from scratch by scanning the tape or bucket and reading every object header back out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frame indexing is what makes partial reads actually fast.&lt;/strong&gt; Combine the Catalog's byte-offset tracking with independent frame boundaries, and you get real range access instead of full-file extraction. HuskHoard's &lt;a href="https://huskhoard.com/blog-post-stream.html" rel="noopener noreferrer"&gt;&lt;code&gt;StreamGate&lt;/code&gt;&lt;/a&gt; HTTP gateway uses this to let you seek into massive files without downloading them wholesale — want to scrub through a 4K video sitting on S3 or a physical LTO tape, in Plex or &lt;code&gt;mpv&lt;/code&gt;? StreamGate resolves the byte range you're asking for, fetches only the relevant frame (an HTTP Range request for cloud, a SCSI tape seek for LTO), and decompresses just that piece. Tape gets its own physics-aware handling on top of this — writes land in 256KB SCSI-aligned blocks with filemarks, specifically to avoid "shoe-shining" the drive, and the Catalog indexes those blocks the same way it indexes cloud frames.&lt;/p&gt;

&lt;p&gt;You can see the shape of this in practice — after installing and pointing the daemon at a test volume, dropping a file into the hot tier and waiting a few seconds is enough to watch it happen:&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="c"&gt;# Drop a file into the watched hot tier&lt;/span&gt;
&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/urandom &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hot_tier/dummy_data.bin &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;12

&lt;span class="c"&gt;# after the janitor interval elapses...&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-ls&lt;/span&gt; hot_tier        &lt;span class="c"&gt;# allocated size drops to ~0, logical size unchanged&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; hot_tier         &lt;span class="c"&gt;# it's become a "husk"&lt;/span&gt;

&lt;span class="c"&gt;# open the file normally — fanotify intercepts the read and recalls it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And streaming straight off tape or cloud without ever touching local disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./target/release/huskhoard &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nt"&gt;--file-path&lt;/span&gt; /media/movies/scifi.mp4 | mpv -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing about that command looks like &lt;a href="https://huskhoard.com/blog-post-archive.html" rel="noopener noreferrer"&gt;archive&lt;/a&gt; recovery. That's the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Have Your Cake and Compress It Too
&lt;/h2&gt;

&lt;p&gt;The underlying idea here isn't really about Zstd, or SQLite, or fanotify specifically — it's that compression shouldn't be a choice between "save money" and "keep my data usable." Those two goals only feel opposed because most tooling makes them opposed. Once compression happens at the infrastructure layer instead of as a manual, user-facing chore, the trade-off disappears.&lt;/p&gt;

&lt;p&gt;If you want to see how the frame layout and TLV headers work in more detail, the &lt;a href="https://github.com/HuskHoard/HuskHoard" rel="noopener noreferrer"&gt;HuskHoard GitHub repo&lt;/a&gt; has the full source (it's Rust, MIT-adjacent AGPL v3, and genuinely readable), and the &lt;a href="https://huskhoard.com/blog.html" rel="noopener noreferrer"&gt;project blog&lt;/a&gt; has deeper dives into specific pieces like the Catalog and the tape format.&lt;/p&gt;

&lt;p&gt;In the meantime, it's worth an honest audit: how much of your own cold, unstructured data is sitting around uncompressed right now — not because compression wouldn't help, but because the tools made it too painful to bother?&lt;/p&gt;

</description>
      <category>storage</category>
      <category>compression</category>
      <category>rust</category>
      <category>devops</category>
    </item>
    <item>
      <title>50% of Cloud Hard Drives are Now SMR: Why Hyperscalers Love What Consumers Hate</title>
      <dc:creator>Joichiro Mitaka</dc:creator>
      <pubDate>Tue, 30 Jun 2026 05:39:23 +0000</pubDate>
      <link>https://dev.to/coldstorage/50-of-cloud-hard-drives-are-now-smr-why-hyperscalers-love-what-consumers-hate-26kb</link>
      <guid>https://dev.to/coldstorage/50-of-cloud-hard-drives-are-now-smr-why-hyperscalers-love-what-consumers-hate-26kb</guid>
      <description>&lt;p&gt;If you hang around r/DataHoarder or home lab communities, you’ve likely heard the golden rule of buying hard drives: &lt;strong&gt;"Avoid SMR at all costs."&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The general consensus is that &lt;a href="https://huskhoard.com/blog-post-smr.html" rel="noopener noreferrer"&gt;Shingled Magnetic Recording&lt;/a&gt; (SMR) drives have abysmal write speeds, fail early, and will crash your RAID arrays. Yet, if you look at the enterprise storage market today, a massive paradox emerges. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftrju2dw9e1jn12eyiyf9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftrju2dw9e1jn12eyiyf9.png" alt=" " width="494" height="463"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;(Caption: Data from Western Digital’s Quarterly Earnings showing UltraSMR crossing 50% of nearline exabyte shipments in 2023)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The numbers don't lie. During recent earnings calls, Western Digital confirmed a massive milestone: their UltraSMR technology now makes up over &lt;strong&gt;50% of their total nearline exabyte shipments&lt;/strong&gt;. Cloud giants like Dropbox rely on SMR for over 90% of their entire storage fleet. In fact, production for high-capacity SMR drives is essentially sold out as AI and data centers buy them by the truckload. &lt;/p&gt;

&lt;p&gt;So, SMR offers higher capacity and a lower price per terabyte. Why does everyone in the consumer and small enterprise space avoid them, and more importantly, how can &lt;em&gt;you&lt;/em&gt; start using them like the cloud giants do?&lt;/p&gt;

&lt;p&gt;Let's demystify SMR technology, look at how the hyperscalers bypassed its limitations, and explore how you can leverage it in your own home lab or small enterprise using modern open-source tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Root of the Stigma: Drive-Managed SMR and the Physics of the Write Head
&lt;/h2&gt;

&lt;p&gt;To understand the hate, you have to understand the physics of the technology. &lt;/p&gt;

&lt;p&gt;Hard drive manufacturers eventually hit a physical wall: &lt;strong&gt;the magnetic write head of a hard drive is physically wider than the read head.&lt;/strong&gt; You need a certain amount of magnetic energy to flip bits on the platter, meaning the write head can only be shrunk so much before it fails to work. However, the &lt;em&gt;read&lt;/em&gt; head can be made incredibly tiny. &lt;/p&gt;

&lt;p&gt;Conventional Magnetic Recording (CMR) accommodates this bulky write head by writing wide data tracks and leaving tiny gaps between them so they don't overlap. &lt;/p&gt;

&lt;p&gt;SMR, on the other hand, exploits the tiny read head. It writes a wide track, and then writes the next track partially overlapping the first—like shingles on a roof. This leaves just enough of the previous track exposed for the smaller read head to read, allowing manufacturers to squeeze up to 25% more data onto the same physical platters.&lt;/p&gt;

&lt;p&gt;The catch? &lt;strong&gt;You cannot randomly overwrite data.&lt;/strong&gt; Because the tracks overlap, modifying a single block of data requires the drive to read a massive "zone" of data into a cache, modify it, and rewrite the entire zone sequentially. &lt;/p&gt;

&lt;p&gt;The stigma started around 2020 when manufacturers silently slipped &lt;strong&gt;Drive-Managed SMR&lt;/strong&gt; into consumer NAS drives. These drives pretended to be normal CMR drives to the host operating system, attempting to manage all that complex "read-modify-write" garbage collection internally. Under heavy, random-write NAS workloads, their caches would fill up, performance would tank to dial-up speeds, and RAID controllers would assume the drives were dead and drop them from arrays.&lt;/p&gt;

&lt;p&gt;Consumers rightly felt burned. But the cloud providers took a completely different approach.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Cloud's Secret Weapon: Host-Managed SMR, Aggressive Tiering, and Pin 3
&lt;/h2&gt;

&lt;p&gt;Hyperscalers like Dropbox and Google didn't try to force SMR drives to act like traditional CMR drives. Instead, they completely rebuilt their storage infrastructure around three core concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Host-Managed Zoned Storage
&lt;/h3&gt;

&lt;p&gt;In a Host-Managed setup, the hard drive doesn't try to hide its shingled nature. It exposes its architecture to the operating system as a &lt;strong&gt;Zoned Block Device (ZBD)&lt;/strong&gt;. The drive essentially says to the host: &lt;em&gt;"Here are my zones. You can only write to them sequentially. If you want to delete or overwrite something, you must reset the entire zone and start from the beginning."&lt;/em&gt; By shifting the responsibility from the drive's underpowered internal processor to the host machine's software, the performance penalty vanishes. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Aggressive Telemetry and Tiering
&lt;/h3&gt;

&lt;p&gt;Rewriting the file system is only half the magic. Cloud services have immense intelligence about your files and access patterns. When you upload a file, it rarely lands directly on a spinning disk; it hits a blazing-fast NVMe cache (the "hot" tier). &lt;/p&gt;

&lt;p&gt;However, the moment their algorithms determine that a file is no longer actively being used—say, a cloud backup from three months ago, or an old photo album—they aggressively tier that data. It is migrated off the expensive flash and funneled sequentially, block by tightly-packed block, into the zones of a high-capacity SMR drive (the "cold" tier). &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Pin 3 Power Management (Letting the Disks Go Cold)
&lt;/h3&gt;

&lt;p&gt;Once that data is packed into an SMR drive, cloud providers take it a step further: they literally pull the plug. &lt;/p&gt;

&lt;p&gt;If you've ever "shucked" an external hard drive for your home lab, you might have run into the infamous &lt;strong&gt;"Pin 3 Power Disable" (PWDIS)&lt;/strong&gt; issue, where you had to put Kapton tape over the 3rd SATA pin just to get the drive to spin up. While consumers were taping over this pin out of frustration, hyperscalers were demanding this feature by design.&lt;/p&gt;

&lt;p&gt;The PWDIS feature allows data centers to remotely hard-reset or cut power to individual drives. When an SMR drive is full of cold data, it doesn't just sit there spinning, consuming electricity, and generating heat. It is commanded to spin down. It sits entirely dormant, acting as a high-density, rapidly accessible tape replacement. It only wakes up when an API call requests a specific dormant file—spinning up, serving the file with a few seconds of acceptable latency, and going back to sleep. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Power of Zoned.io
&lt;/h3&gt;

&lt;p&gt;If you want to dive deep into how the foundation of this works on a technical level, the ultimate resource is &lt;a href="https://zonedstorage.io/" rel="noopener noreferrer"&gt;Zoned.io&lt;/a&gt;. Backed by major storage players, it documents the Linux kernel's implementation of Zoned Block Devices. Zoned Storage is becoming critical not just for SMR hard drives, but for next-generation NVMe SSDs (ZNS) that use the exact same sequential-write paradigms to reduce write amplification and extend flash memory life.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bringing SMR to the Home Lab and Small Enterprise
&lt;/h2&gt;

&lt;p&gt;You don't need to be AWS or Dropbox to take advantage of SMR pricing and density. You just need to pay attention to your workload requirements and use software designed to treat storage sequentially.&lt;/p&gt;

&lt;p&gt;SMR is an absolute powerhouse for &lt;strong&gt;WORM (Write Once, Read Many)&lt;/strong&gt; workloads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Media archiving and Plex servers&lt;/li&gt;
&lt;li&gt;  Immutable backup targets&lt;/li&gt;
&lt;li&gt;  Log aggregation and long-term surveillance video storage&lt;/li&gt;
&lt;li&gt;  Data hoarding and cold storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are using ZFS, you can utilize sequential-friendly configurations, but to truly unlock the potential of Host-Managed SMR in a small enterprise or home lab, you need purpose-built archiving tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Do's and Don'ts of SMR &lt;a href="https://huskhoard.com/blog-post-archive.html" rel="noopener noreferrer"&gt;Archiving&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;SMR is an absolute powerhouse for WORM (Write Once, Read Many) workloads, but only if you follow the rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Don'ts: RAID, ZFS, and Standard File Systems
&lt;/h3&gt;

&lt;p&gt;The biggest mistake you can make is blindly handing an SMR drive over to your operating system or RAID controller.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Don't use hardware RAID or standard ZFS.&lt;/strong&gt; RAID arrays (and ZFS RAIDZ) rely on parity calculations and constant metadata updates. If an array needs to rebuild or "resilver", it blasts the drives with random, fragmented writes. The SMR cache will choke, write speeds will drop to practically zero, and the controller will drop the drive entirely.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Don't just format it to ext4 or NTFS.&lt;/strong&gt; Traditional file systems are designed for random I/O. Even if you are just sequentially copying a large video file, the file system is constantly darting its write head back and forth across the platter to update the journal, timestamps, and file allocation tables. This micro-random I/O ruins SMR performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Do's: Buffer, Batch, and Stream
&lt;/h3&gt;

&lt;p&gt;Instead of fighting the drive with random I/O, treat it exactly like a modern tape drive.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Do use an SSD staging tier.&lt;/strong&gt; Borrow the cloud provider's playbook. Buffer your incoming files on an NVMe or standard SSD first. Once you have accumulated a large batch of cold data, flush it to the SMR drive in one massive, continuous stream.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Do use Zoned-aware storage layers.&lt;/strong&gt; The Linux kernel has made massive strides here. For example, &lt;code&gt;btrfs&lt;/code&gt; now has a dedicated "Zoned Mode" explicitly designed to work with Zoned Block Devices. It forces all writes to be strictly sequential, keeping metadata organized and eliminating the read-modify-write penalty.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to go deeper into SMR this is a good resource(&lt;a href="https://huskhord.com/blog-post-smr.html" rel="noopener noreferrer"&gt;Stop Murdering your SMR Drives&lt;/a&gt;), but if you want to take advantage of SMR pricing and density today, you must completely rethink your storage architecture.&lt;/p&gt;

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

&lt;p&gt;The storage industry is moving toward Zoned Storage, whether consumers like it or not. With the massive shift toward UltraSMR in the enterprise space, the era of ignoring the technology is over.&lt;/p&gt;

&lt;p&gt;It's time we stop universally hating SMR drives and start treating them like the specialized, high-capacity archiving tools they are. If you stop handing them directly to standard file systems and instead provide them with the buffered, sequential workloads they crave, they will reward you with massive capacity, incredible cost savings, and a much lower power bill.&lt;/p&gt;

&lt;p&gt;Check out the documentation at &lt;a href="https://zonedstorage.io/" rel="noopener noreferrer"&gt;Zoned.io&lt;/a&gt; to understand the kernel-level magic, and if you are looking for an out-of-the-box archiving solution that loves SMR drives, check out  AGPL archiving at &lt;a href="https://huskhord.com" rel="noopener noreferrer"&gt;Huskhord.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>linux</category>
      <category>rust</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Addressing the global namespace</title>
      <dc:creator>Joichiro Mitaka</dc:creator>
      <pubDate>Thu, 25 Jun 2026 06:20:12 +0000</pubDate>
      <link>https://dev.to/coldstorage/addressing-the-global-namespace-1k21</link>
      <guid>https://dev.to/coldstorage/addressing-the-global-namespace-1k21</guid>
      <description>&lt;p&gt;There is a moment in every data hoarder's life — and in every small media shop's IT history — when the first archive drive fills up. You slot in a second tape, plug in another external drive, configure a new cloud bucket. Problem solved. &lt;/p&gt;

&lt;p&gt;Except now you have a different problem: &lt;strong&gt;you have no idea where anything is.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not in the "I lost the file" sense. More in the "I know I have that file somewhere across these six volumes, but I don't know which one, and I'm not sure I haven't archived it twice, and that third copy might be the old version" sense. The data is there. The knowledge of where it is, and which copy is canonical, is not.&lt;/p&gt;

&lt;p&gt;This is the multi-volume namespace problem. It's been lurking in storage management since reel-to-reel tape in the 1960s, and the solutions to it span from "I have a spreadsheet" to "I have a $200,000 enterprise storage cluster." Most people end up somewhere in the uncomfortable middle.&lt;/p&gt;

&lt;p&gt;Let's look at the problem properly, put some numbers on it, and walk through what people actually do — and why each approach eventually runs out of road.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Numbers First
&lt;/h2&gt;

&lt;p&gt;"Multiple volumes" sounds modest. It isn't, once you start measuring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LTO tape capacity by generation (native / 2.5:1 compressed):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;Native&lt;/th&gt;
&lt;th&gt;Compressed&lt;/th&gt;
&lt;th&gt;$/TB (cartridge only, approx.)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LTO-7&lt;/td&gt;
&lt;td&gt;6 TB&lt;/td&gt;
&lt;td&gt;15 TB&lt;/td&gt;
&lt;td&gt;~$0.02/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LTO-8&lt;/td&gt;
&lt;td&gt;12 TB&lt;/td&gt;
&lt;td&gt;30 TB&lt;/td&gt;
&lt;td&gt;~$0.015/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LTO-9&lt;/td&gt;
&lt;td&gt;18 TB&lt;/td&gt;
&lt;td&gt;45 TB&lt;/td&gt;
&lt;td&gt;~$0.012/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LTO-10&lt;/td&gt;
&lt;td&gt;40 TB&lt;/td&gt;
&lt;td&gt;100 TB&lt;/td&gt;
&lt;td&gt;(2026, available now)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A serious homelab data hoarder with a modest LTO-8 drive will fill a 12 TB cartridge in 6–18 months depending on what they're collecting. A four-tape rotation is a realistic minimum for anyone who takes offline copies seriously. That's 48 TB native — multiple volumes by definition.&lt;/p&gt;

&lt;p&gt;Scale up to a small production company, a university research group, or a regional broadcaster. Now you have dozens of tapes, offline drives at an off-site location, and a cloud backend for disaster recovery. Each one a separate island of storage.&lt;/p&gt;

&lt;p&gt;Hard drive archives have the same problem. A rotating set of 14–20 TB USB drives for cold backup is cheap and practical. It's also, over five years, eight or ten drives with no shared index.&lt;/p&gt;

&lt;p&gt;The problem isn't finding the storage. The problem is that the more storage you buy, the worse your ability to reason about it gets — unless you build something to manage the namespace across all of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What "Namespace" Means in This Context
&lt;/h2&gt;

&lt;p&gt;A namespace is simply a unified view of your files. On a single volume, it's trivial: mount the drive, run &lt;code&gt;ls&lt;/code&gt;, see everything. The file system &lt;em&gt;is&lt;/em&gt; the namespace.&lt;/p&gt;

&lt;p&gt;The moment you have two volumes, the file system isn't enough. A file might be on either one. If both are mounted simultaneously, you have two separate trees. If one is offline, its files are invisible. If the same logical file has been archived to both volumes at different points in time, you have two physical copies with no built-in way to determine which is current.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ideal global namespace would let you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List every file you've ever archived, regardless of which physical volume holds it&lt;/li&gt;
&lt;li&gt;Know immediately which volume a file is on, and what its current version is&lt;/li&gt;
&lt;li&gt;Know that you haven't accidentally archived the same content twice on different volumes&lt;/li&gt;
&lt;li&gt;Retrieve the right version without checking six different drives one by one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No single standard delivers all four of these, which is why the solutions below exist — and why each one is only a partial answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Approach 1: LTFS — A File System on Every Tape
&lt;/h2&gt;

&lt;p&gt;The closest thing to a proper multi-generation standard for tape namespace management is &lt;strong&gt;LTFS&lt;/strong&gt; (Linear Tape File System), adopted by the LTO Consortium in 2010 and standardized by SNIA. If you're running LTO-5 or later, you can format any cartridge as LTFS and mount it exactly like a USB drive.&lt;/p&gt;

&lt;p&gt;The way it works is elegant. LTFS divides a tape cartridge into two partitions. Partition 0 is the index — an XML document describing your directory tree, file names, timestamps, and the exact tape block where each file's data begins. Partition 1 is the data itself. When you insert an LTFS tape and run the mount command, the drive reads Partition 0 into memory, and your OS presents the tape as a browsable volume.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ltfs &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;devname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 /mnt/tape
&lt;span class="nb"&gt;ls&lt;/span&gt; /mnt/tape/projects/alpha/
&lt;span class="c"&gt;# rushes/  finals/  audio/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can drag and drop. You can &lt;code&gt;cp&lt;/code&gt;. Files are written sequentially to the end of the data partition and the index is updated. It's a genuine open standard — an LTFS tape written on Linux can be read on macOS or Windows with compatible software, with no proprietary software in the middle.&lt;/p&gt;

&lt;p&gt;For interchange and manual archiving, LTFS is excellent. If you're sending a tape to a collaborator or to a facility you've never worked with before, LTFS is the right choice. It is genuinely self-describing: the metadata travels with the media.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where LTFS Stops
&lt;/h3&gt;

&lt;p&gt;The problem is that LTFS solves the per-tape namespace and stops there. &lt;strong&gt;Each tape is its own island.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a tape is offline — sitting on a shelf — its files are invisible. There is no LTFS spanning standard that lets you query across multiple cartridges simultaneously. If you have four tapes and need to find a file from 2022, you are mounting tapes until you find it, or maintaining a separate external record of what's where.&lt;/p&gt;

&lt;p&gt;LTFS also inherits tape's fundamental constraint: files can only be appended, never overwritten. If you delete a file in the LTFS index, the data blocks on tape are simply marked unavailable — the space is not recovered until you reformat the entire cartridge. More importantly for long-lived archives: &lt;strong&gt;LTFS has no native versioning.&lt;/strong&gt; The spec doesn't define what a version is or how to track one. If you archive &lt;code&gt;final.mov&lt;/code&gt;, re-edit it, and archive it again to the same tape, the second write is a separate file entry in the same index. Version control becomes your problem.&lt;/p&gt;

&lt;p&gt;The index partition itself has a ceiling — roughly 5–10% of tape capacity — which can become a bottleneck for archives with millions of small files.&lt;/p&gt;

&lt;p&gt;For many users, these aren't dealbreakers for a single-tape workflow. But the moment you're managing a tape library, you need something sitting above LTFS to provide the cross-volume view. LTFS gives you a great building block. It doesn't give you a global namespace.&lt;/p&gt;




&lt;h2&gt;
  
  
  Approach 2: The Spreadsheet (And Its Cousins)
&lt;/h2&gt;

&lt;p&gt;The most common approach in homelabs and small shops isn't LTFS or enterprise software. It's a spreadsheet. Or a text file. Or a Notion database. Or a Python script someone wrote in a weekend that generates a CSV of filenames and tape labels.&lt;/p&gt;

&lt;p&gt;This approach is completely understandable and immediately comprehensible to everyone on the team. "Tape 003 has the 2023 raw footage. Tape 007 has the 2024 project deliverables. Check the sheet."&lt;/p&gt;

&lt;p&gt;It also has a half-life of about six months.&lt;/p&gt;

&lt;p&gt;The spreadsheet tracks what you intended to put on each tape at the moment you archived it. It doesn't automatically update when files change. It doesn't track versions — when you re-archive an updated file, do you add a new row? Update the existing one? Add a note? Different people make different decisions, and over time the spreadsheet becomes an archaeological record of past intentions rather than a live index.&lt;/p&gt;

&lt;p&gt;Deduplication is purely manual. Nothing in the spreadsheet warns you that &lt;code&gt;client_deliverable_FINAL_v3.mov&lt;/code&gt; on Tape 003 and &lt;code&gt;client_deliverable_FINAL_v3_2.mov&lt;/code&gt; on Tape 009 are the same underlying content, archived twice by different team members six weeks apart.&lt;/p&gt;

&lt;p&gt;Search is grep over a CSV or a Notion filter — workable for hundreds of files, painful for tens of thousands, and broken for millions.&lt;/p&gt;

&lt;p&gt;The spreadsheet approach is a record of what someone thinks is on the tapes. The tapes themselves know what's actually on them. These two sources of truth diverge over time, and the divergence accelerates as the archive grows.&lt;/p&gt;

&lt;p&gt;Some teams move from spreadsheets to proper databases — SQLite or Postgres — and write custom tooling to maintain the index. This is a genuine improvement. The index is now queryable, consistent, and maintainable. But custom tooling requires ownership: someone has to build it, update it when formats change, and ensure it stays in sync with the physical media. Most teams don't have that person, or lose them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Approach 3: Enterprise HSM
&lt;/h2&gt;

&lt;p&gt;The "correct" industrial answer to this problem is a &lt;strong&gt;Hierarchical Storage Management (HSM)&lt;/strong&gt; system. HSM has existed since IBM implemented it for mainframes in 1978. The idea is straightforward: files on fast, expensive storage automatically migrate to slower, cheaper storage based on access patterns, and transparent stub files replace them so users never notice the transition.&lt;/p&gt;

&lt;p&gt;Modern commercial HSM products — IBM Spectrum Protect, HPE Data Management Framework, Quantum StorNext — provide exactly the global namespace you need. You have a single directory tree. Files that haven't been touched in 90 days silently move to tape. Open one, and the system recalls it transparently. The catalog tracks every file, every version, every volume.&lt;/p&gt;

&lt;p&gt;They work. They work very well. They're also built for organizations with dedicated storage administrators, SAN infrastructure, enterprise support contracts, and storage budgets in the low-to-mid six figures.&lt;/p&gt;

&lt;p&gt;Enterprise HSM is designed for the assumption that you have a team, a budget, and a vendor relationship. The software itself can cost more than a homelab enthusiast's entire NAS. The hardware requirements (dedicated tape libraries, Fibre Channel HBAs, redundant metadata servers) further narrow the addressable audience.&lt;/p&gt;

&lt;p&gt;For a small production company with four LTO drives and a NAS, enterprise HSM is a missile to kill a mosquito. And there is no meaningful open-source equivalent in this space — the gap between "hobbyist script" and "enterprise HSM" has historically been a cliff with nothing in the middle.&lt;/p&gt;




&lt;h2&gt;
  
  
  Approach 4: Backup Software (The Wrong Tool)
&lt;/h2&gt;

&lt;p&gt;A common misconception: "I'll just use my backup software to manage my archive." Backup software — Veeam, Bacula, Duplicati, Restic, Amanda — is excellent at what it does, which is taking point-in-time snapshots and storing them in a recoverable format.&lt;/p&gt;

&lt;p&gt;It is not designed for an archive, and the distinction matters.&lt;/p&gt;

&lt;p&gt;A backup is a recovery mechanism. You restore from it when something goes wrong. The interface is: "restore system/file to state as of [date]." Backup software maintains its own catalog of backup sets, not a live representation of your file namespace.&lt;/p&gt;

&lt;p&gt;An archive is a primary store. Files live there permanently, and you access them directly. The interface should be: "open this file" — and it should just work, regardless of which volume holds the bytes.&lt;/p&gt;

&lt;p&gt;Backup software can hold your &lt;a href="https://huskhoard.com/blog-post-archive.html" rel="noopener noreferrer"&gt;archival data&lt;/a&gt;. It will not give you a unified browsable namespace across multiple tapes. It will not transparently serve a file from tape when an application requests it. It will not tell you whether a file is already archived before you try to archive it again. These are the functions a real archive system needs to provide, and backup software isn't designed to provide them.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Ideal Solution Would Look Like
&lt;/h2&gt;

&lt;p&gt;At this point the shape of the problem is clear, and you can start to describe what a complete solution would need to provide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Volume identity that outlives device paths.&lt;/strong&gt; If your LTO drive moves from &lt;code&gt;/dev/nst0&lt;/code&gt; to &lt;code&gt;/dev/nst1&lt;/code&gt; because you rearranged your HBA, every restore should still work. Volume identity needs to be based on something burned into the media at format time — a UUID — not a device path or a human-assigned label.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A &lt;a href="http://huskhoard.com/blog-post-cat.html" rel="noopener noreferrer"&gt;catalog&lt;/a&gt; that is separate from any single volume.&lt;/strong&gt; You need a central index that can answer "where is this file?" without mounting every volume in your library. The catalog holds the address (volume UUID + byte offset) for every archived file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But also: volumes that are self-describing.&lt;/strong&gt; The catalog is the fast path. But if you lose the catalog database, you should be able to reconstruct it from the physical media — because each volume should carry enough metadata to identify every object it contains. The catalog is an optimization, not a dependency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A namespace that is always visible, regardless of volume state.&lt;/strong&gt; Files that are on offline tapes should still appear in your OS. You should be able to see the file, read its metadata, and — when you try to open it — get a useful message about which volume to insert, not a confusing "file not found" error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://huskhoard.com/blog-post-blake.html" rel="noopener noreferrer"&gt;Hash-based&lt;/a&gt; deduplication, not path-based.&lt;/strong&gt; The system should know whether a file has already been archived by its content, not just its name. Renaming a file shouldn't trigger a redundant archive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://huskhoard.com/blog-post-version.html" rel="noopener noreferrer"&gt;Versioning&lt;/a&gt; as a first-class concept.&lt;/strong&gt; When a file changes and is re-archived, both the old and new versions should be tracked and retrievable. The system should know that version 3 is on Tape C and version 1 is on Tape A, and serve the right one by default while making rollback possible on request.&lt;/p&gt;

&lt;p&gt;None of the approaches above deliver all six of these properties. LTFS gets you two or three. Spreadsheets get you one on a good day. Enterprise HSM gets you all six, but only if you have an enterprise budget.&lt;/p&gt;




&lt;h2&gt;
  
  
  An Open-Source Answer
&lt;/h2&gt;

&lt;p&gt;This gap — between hobbyist scripts and enterprise HSM — is where a relatively new open-source project called &lt;strong&gt;&lt;a href="https://github.com/HuskHoard/HuskHoard" rel="noopener noreferrer"&gt;HuskHoard&lt;/a&gt;&lt;/strong&gt; is trying to operate.&lt;/p&gt;

&lt;p&gt;HuskHoard is a data-tiering engine built in Rust for Linux. It uses the &lt;code&gt;fanotify&lt;/code&gt; kernel API to transparently stub cold files — replacing them with zero-byte placeholders while keeping them visible in your file system — and automatically recalls the data from the appropriate volume when an application opens the file. It supports LTO tape drives natively (via the SCSI tape driver), flat image files, external drives, and cloud backends via &lt;code&gt;rclone&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The catalog is a SQLite database that tracks every archived file by its content hash, its version history, and its exact byte offset on a specific volume UUID — not a device path. Plug a drive into a different port, and the catalog heals the mapping automatically via a rescan. Volumes carry their own self-describing headers, so the catalog can be fully rebuilt from raw media if it's ever lost.&lt;/p&gt;

&lt;p&gt;For multi-volume management specifically: when a needed volume is offline, HuskHoard reports the volume's UUID and the human-readable &lt;code&gt;location_hint&lt;/code&gt; you've assigned it ("Shelf A, Slot 2 — LTO-9 Vol 001"), then waits for you to insert it. It won't re-archive a file that's already in the catalog with a matching content hash, regardless of which volume holds the original. Each volume is formatted once with a UUID, and that identity persists for the life of the media.&lt;/p&gt;

&lt;p&gt;It's not enterprise HSM. The robotic tape library automation, the Fibre Channel fabric, the dedicated metadata servers — those aren't there. But for a homelab operator, a small production company, or any team that has more data than fits on one thing and needs a coherent answer to "where is that file?", it addresses all six requirements above as open-source software, free under AGPL v3.&lt;/p&gt;

&lt;p&gt;The architecture notes and a deeper dive into how the catalog handles the multi-volume problem are on the &lt;a href="https://huskhoard.com/blog.html" rel="noopener noreferrer"&gt;HuskHoard blog&lt;/a&gt; if you want to go further.&lt;/p&gt;




&lt;p&gt;The multi-volume namespace problem isn't glamorous. It doesn't show up in benchmarks or feature comparison tables. It shows up the third time you're manually mounting tapes trying to find a project file from 18 months ago, or the moment you realize you've been archiving the same directory to two different drives for six months and have no idea which copies are current.&lt;/p&gt;

&lt;p&gt;Every solution in this space involves a catalog — some record of what is where. The question is whether that catalog is a spreadsheet you maintain manually, a per-tape index that goes dark when the tape goes offline, an enterprise product with an enterprise price tag, or something designed to sit in the middle and make this problem manageable for the rest of us.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>rust</category>
      <category>aws</category>
      <category>software</category>
    </item>
    <item>
      <title>Using Zstd Frames to Egress Partial Parquet Files</title>
      <dc:creator>Joichiro Mitaka</dc:creator>
      <pubDate>Wed, 24 Jun 2026 15:26:22 +0000</pubDate>
      <link>https://dev.to/coldstorage/using-zstd-frames-to-egress-partial-parquet-files-1fdd</link>
      <guid>https://dev.to/coldstorage/using-zstd-frames-to-egress-partial-parquet-files-1fdd</guid>
      <description>&lt;h2&gt;
  
  
  Jump Tables, TLV Footers, and the Real Cost of Reading What You Don't Need
&lt;/h2&gt;

&lt;p&gt;You're paying for bytes you never read.&lt;/p&gt;

&lt;p&gt;A data engineer on a busy pipeline touches dozens of Parquet files a day: schema discovery, predicate pushdown, column pruning, metadata scrapes for a data catalog sync. In each case, the application needs maybe 200 KB of context from a file that is 4 GB on disk. Without a seekable archive format and a jump table to find the right frame, your HTTP client fetches the whole thing, and your cloud egress invoice reflects every unnecessary gigabyte.&lt;/p&gt;

&lt;p&gt;This post quantifies the problem, then walks through how &lt;a href="https://github.com/HuskHoard/HuskHoard" rel="noopener noreferrer"&gt;HuskHoard&lt;/a&gt; uses seekable Zstd frames, a per-volume jump table, and TLV-encoded footer metadata to make partial egress a first-class citizen across multi-volume archives — disk, cloud, and LTO tape alike.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem, In Dollars
&lt;/h2&gt;

&lt;p&gt;S3 standard egress runs $0.09/GB. GCS is $0.08/GB. Even Cloudflare R2, which is free for egress &lt;em&gt;from R2 to the internet&lt;/em&gt;, still costs you in latency and API call count when you cannot bound the range of bytes you need.&lt;/p&gt;

&lt;p&gt;Here is a representative read pattern for a cold analytics archive:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Bytes Needed&lt;/th&gt;
&lt;th&gt;Bytes Fetched (naïve)&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Schema discovery&lt;/td&gt;
&lt;td&gt;~50 KB (Parquet footer)&lt;/td&gt;
&lt;td&gt;1–8 GB (full file)&lt;/td&gt;
&lt;td&gt;~1:16,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single column scan&lt;/td&gt;
&lt;td&gt;~200 MB (one column chunk)&lt;/td&gt;
&lt;td&gt;4 GB (full row group)&lt;/td&gt;
&lt;td&gt;1:20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data catalog sync (1M files)&lt;/td&gt;
&lt;td&gt;~50 GB (footers only)&lt;/td&gt;
&lt;td&gt;~4 PB (full files)&lt;/td&gt;
&lt;td&gt;1:80,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selective restore (1 row group)&lt;/td&gt;
&lt;td&gt;~128 MB&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;td&gt;1:32&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On 100 TB of cold Parquet data with $0.09/GB egress:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full read for schema sync&lt;/strong&gt;: 100 TB × $0.09 = &lt;strong&gt;$9,216&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial read (footers only, avg 100 KB/file, 1M files)&lt;/strong&gt;: ~100 GB × $0.09 = &lt;strong&gt;$9.00&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Savings per catalog sync: $9,207 — 99.9% reduction&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even a conservative column-scan scenario (pulling 15% of each file's bytes) cuts a $9,216 monthly read bill to &lt;strong&gt;$1,382&lt;/strong&gt;. The ceiling on savings is determined entirely by how precisely you can address the bytes you actually need.&lt;/p&gt;

&lt;p&gt;That precision is what frames and jump tables buy you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Zstd Frames: What They Are and Why They Matter
&lt;/h2&gt;

&lt;p&gt;A single &lt;code&gt;.zst&lt;/code&gt; file produced by the standard &lt;code&gt;zstd&lt;/code&gt; CLI is one frame. Everything inside is a single compressed stream. You have to start decompression at byte 0 to reach any byte inside.&lt;/p&gt;

&lt;p&gt;But the Zstd spec allows a concatenation of independent frames. Each frame is a complete, self-contained unit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Frame 0][Frame 1][Frame 2]...[Frame N]
 ^         ^         ^           ^
 16 MB     16 MB     16 MB       partial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every frame has a known &lt;code&gt;compressed_size&lt;/code&gt; and &lt;code&gt;decompressed_size&lt;/code&gt;. If you know those sizes in advance (stored in a jump table), you can seek directly to Frame N by summing the compressed sizes of frames 0 through N-1. You never decompress anything you don't need. Frame N is fetched with a single HTTP Range request, decompressed independently, and the relevant bytes are piped downstream.&lt;/p&gt;

&lt;p&gt;This is the architectural core of HuskHoard's egress model, and it maps cleanly onto how the Parquet format itself carves up a file.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Parquet Parallel: Row Groups as Frames
&lt;/h2&gt;

&lt;p&gt;Parquet is deliberately designed for partial reads. A Parquet file contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Row groups&lt;/strong&gt; — horizontal partitions of the data, each independently readable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Column chunks&lt;/strong&gt; — vertical slices within a row group&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page headers&lt;/strong&gt; — per-page metadata within each column chunk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Footer&lt;/strong&gt; — the &lt;code&gt;FileMetaData&lt;/code&gt; Thrift struct at the end of the file, containing the schema, row group offsets, column statistics, and key-value metadata. Preceded by a 4-byte footer length and terminated with the magic bytes &lt;code&gt;PAR1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A reader that wants only the footer performs two range requests: one to get the last 8 bytes (magic + footer length), and one to get the footer itself. Everything else stays on the remote. A reader that wants one column from one row group consults the footer to find the column chunk's byte offset and length, then fires a single range request.&lt;/p&gt;

&lt;p&gt;HuskHoard's frame model mirrors this exactly, but at the archive level rather than within a single Parquet file.&lt;/p&gt;




&lt;h2&gt;
  
  
  HuskHoard's Implementation: Frames, the Catalog, and the Jump Table
&lt;/h2&gt;

&lt;p&gt;When HuskHoard archives a file to any backend — a flat image file acting as a tape volume, a physical LTO cartridge, or an rclone cloud remote — it writes the payload as a sequence of 16 MB Zstd frames. For each frame, it records the mapping between uncompressed byte position and compressed byte position on the volume.&lt;/p&gt;

&lt;p&gt;That mapping is the &lt;code&gt;object_frames&lt;/code&gt; table in &lt;code&gt;husk_catalog.db&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;object_frames&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;file_path&lt;/span&gt;           &lt;span class="nb"&gt;TEXT&lt;/span&gt;    &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;version&lt;/span&gt;             &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;uncompressed_offset&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- where this frame starts in the original file&lt;/span&gt;
    &lt;span class="n"&gt;compressed_offset&lt;/span&gt;   &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- where this frame starts on the storage volume&lt;/span&gt;
    &lt;span class="n"&gt;compressed_size&lt;/span&gt;     &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;    &lt;span class="c1"&gt;-- how many bytes to fetch from the volume&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;idx_frames&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;object_frames&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;version&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the jump table. Given a byte range request for &lt;code&gt;bytes=2147483648-2281701376&lt;/code&gt; (a 128 MB window starting at the 2 GB mark), the gateway does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;compressed_offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compressed_size&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;object_frames&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/warehouse/events/2024-01-01.parquet'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="k"&gt;version&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;object_frames&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="n"&gt;uncompressed_offset&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2147483648&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;uncompressed_offset&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One row. One seek. One range request against the volume. Everything else stays dark.&lt;/p&gt;

&lt;p&gt;The HTTP gateway loop in StreamGate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Range request arrives (bytes=X-Y)
        │
        ▼
Query object_frames → nearest frame boundary ≤ X
        │
        ▼
Seek to compressed_offset on volume (tape block, S3 range, local seek)
        │
        ▼
Decompress forward to exact byte X, stream through Y
        │
        ▼
Client receives exactly what it asked for
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a 4K video file seeking to the 2-hour mark, this is why &lt;code&gt;mpv&lt;/code&gt; can start playing from tape or S3 in under a second instead of waiting for a multi-gigabyte download.&lt;/p&gt;




&lt;h2&gt;
  
  
  TLV Footers: Turning the Frame Header Into a Parquet-Style Catalog Entry
&lt;/h2&gt;

&lt;p&gt;Every file archived by HuskHoard is preceded on the storage volume by a strict &lt;strong&gt;4,096-byte ObjectHeader&lt;/strong&gt;. The first 136 bytes carry the fixed-width mechanics: a magic string (&lt;code&gt;USTDHUSK&lt;/code&gt;), the file's UUID, POSIX permissions, BLAKE3 hash, compressed and uncompressed sizes, and a CRC32 of the header itself.&lt;/p&gt;

&lt;p&gt;The remaining &lt;strong&gt;3,960 bytes&lt;/strong&gt; are dedicated to &lt;strong&gt;TLV (Type-Length-Value)&lt;/strong&gt; encoded metadata — the same binary framing used in X.509 certificates, SNMP, and dozens of wire protocols chosen specifically because unknown type codes can be safely skipped by any forward-compatible parser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Byte  0 –  7: Magic "USTDHUSK"
Byte  8 – 23: Volume UUID (16 bytes)
Byte 24 – 55: BLAKE3 hash (32 bytes)
Byte 56 – 63: Uncompressed payload size
Byte 64 – 71: Compressed payload size
Byte 72 – 79: Original mtime
Byte 80 – 83: POSIX mode
Byte 84 – 87: Header CRC32
Byte 88 –135: File path (null-terminated, 48 bytes max inline)
Byte 136–4095: TLV region (3,960 bytes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A TLV tag entry looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Type: u8][Key-Length: u16][Key: bytes][Value-Length: u32][Value: bytes]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the Parquet footer analogy becomes structural rather than metaphorical. For a Parquet file being archived, HuskHoard can embed the Parquet &lt;code&gt;FileMetaData&lt;/code&gt; statistics directly into this TLV region:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TLV Type&lt;/th&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parquet.schema&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Serialized Thrift schema (JSON or binary)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parquet.row_count&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total row count as little-endian u64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parquet.col.event_ts.min&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minimum value of &lt;code&gt;event_ts&lt;/code&gt; column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parquet.col.event_ts.max&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum value of &lt;code&gt;event_ts&lt;/code&gt; column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parquet.col.user_id.null_count&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Null count for &lt;code&gt;user_id&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parquet.row_group.count&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of row groups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;workflow.pipeline&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"ingest_v3"&lt;/code&gt; — POSIX xattr from source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;workflow.owner&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"data-eng-team"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These statistics travel &lt;strong&gt;physically bonded to the data&lt;/strong&gt; on every storage medium — disk image, tape cartridge, S3 object. If the SQLite catalog is lost, &lt;code&gt;husk rebuild&lt;/code&gt; walks the volume, reads every 4 KB header, and reconstructs the catalog complete with all column statistics. The tape is entirely self-describing.&lt;/p&gt;

&lt;p&gt;But the real payoff is what this enables while the catalog &lt;em&gt;is&lt;/em&gt; present.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-Volume Catalog Queries: Pruning at the Volume Level
&lt;/h2&gt;

&lt;p&gt;A production HuskHoard deployment might span several volumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Volume A (tape, 12 TB) — archive 2022–2023
Volume B (tape, 12 TB) — archive 2023–2024
Volume C (NVMe image, 2 TB) — archive 2024–present
Volume D (S3:us-east-1, 50 TB) — cloud replica
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;catalog&lt;/code&gt; table records which volume holds each archived version of each file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tape_uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;-- identifies the volume&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tape_offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;-- byte offset of the ObjectHeader on that volume&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compressed_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom_metadata&lt;/span&gt;     &lt;span class="c1"&gt;-- mirrors the TLV tags as JSON&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="k"&gt;catalog&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;json_extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom_metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'$.parquet.col.event_ts.min'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="n"&gt;json_extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom_metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'$.parquet.col.event_ts.max'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-31'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="n"&gt;json_extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom_metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'$.parquet.row_count'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query executes in milliseconds against the SQLite catalog on your SSD. The tape drives stay spun down. S3 is never contacted. You get back a list of &lt;code&gt;(file_path, tape_uuid, tape_offset)&lt;/code&gt; tuples — the exact volumes and positions to touch.&lt;/p&gt;

&lt;p&gt;Then, per file, for each column you actually need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;compressed_offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compressed_size&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;object_frames&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;file_path&lt;/span&gt;           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/warehouse/events/2024-01-15.parquet'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="k"&gt;version&lt;/span&gt;             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="n"&gt;uncompressed_offset&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;col_chunk_start&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;col_chunk_end&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;uncompressed_offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You issue a range request for only those frames. For a 4 GB Parquet file where you need one 200 MB column chunk:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Data Transferred&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Catalog query (SQLite, local)&lt;/td&gt;
&lt;td&gt;0 bytes egress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;object_frames lookup (SQLite, local)&lt;/td&gt;
&lt;td&gt;0 bytes egress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP Range to S3 (compressed frame bytes)&lt;/td&gt;
&lt;td&gt;~85 MB (at 2.4:1 Zstd ratio)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total vs naïve full-file fetch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;85 MB vs 1.7 GB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is a &lt;strong&gt;95% reduction&lt;/strong&gt; on a per-query basis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting Numbers to the Savings
&lt;/h2&gt;

&lt;p&gt;Let's use a concrete scenario: a data team maintains a 10 TB cold Parquet archive on S3, with an average file size of 4 GB. They run three workloads:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workload A — Nightly catalog sync (schema + statistics only)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Files: 2,500 Parquet files&lt;/li&gt;
&lt;li&gt;Data needed per file: footer only (~150 KB each)&lt;/li&gt;
&lt;li&gt;Total needed: ~375 MB&lt;/li&gt;
&lt;li&gt;Full-file cost: 10 TB × $0.09 = &lt;strong&gt;$921.60/month&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Partial-frame cost: 375 MB × $0.09 = &lt;strong&gt;$0.03/month&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monthly savings: $921.57&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Workload B — Ad-hoc column scan (one column across 20% of files)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Files queried: 500 (selected by TLV statistics predicate)&lt;/li&gt;
&lt;li&gt;Column chunk per file: ~200 MB uncompressed → ~85 MB compressed frames&lt;/li&gt;
&lt;li&gt;Total fetched: ~42.5 GB&lt;/li&gt;
&lt;li&gt;Full-file cost: 500 × 4 GB × $0.09 = &lt;strong&gt;$180.00/query&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Partial-frame cost: 42.5 GB × $0.09 = &lt;strong&gt;$3.83/query&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-query savings: $176.17 (97.9%)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Workload C — Point-in-time restore of a single row group&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 file × 1 row group = 128 MB uncompressed → ~54 MB compressed&lt;/li&gt;
&lt;li&gt;Full-file cost: 4 GB × $0.09 = &lt;strong&gt;$0.36&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Partial-frame cost: 54 MB × $0.09 = &lt;strong&gt;$0.005&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-restore savings: $0.355 (98.6%)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At scale, Workload A alone — a nightly catalog sync that most teams run without thinking about the bill — generates &lt;strong&gt;~$11,000/year in unnecessary egress&lt;/strong&gt; on a 10 TB archive. The frame-indexed approach reduces that to under $1/year.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Self-Describing Volume: Your Catalog Backup Is on the Tape
&lt;/h2&gt;

&lt;p&gt;One underappreciated consequence of storing TLV column statistics in every ObjectHeader is that the volume itself becomes a data catalog. After a complete disaster recovery (catalog database lost, fresh server), &lt;code&gt;husk rebuild&lt;/code&gt; walks the storage volume 4 KB at a time, reads every ObjectHeader, validates the CRC32, and inserts a new catalog row including all TLV-encoded metadata — column statistics, schema, pipeline tags, everything.&lt;/p&gt;

&lt;p&gt;The catalog is not a separate system that the archive depends on. The catalog is a cache that accelerates access to information already encoded in the archive itself. This is the same philosophical commitment Parquet makes: the footer is not a separate sidecar file; it is part of the format.&lt;/p&gt;

&lt;p&gt;For teams integrating with external data catalogs (Apache Atlas, Hive Metastore, Unity Catalog), this means HuskHoard can emit catalog events on &lt;code&gt;husk rebuild&lt;/code&gt; just as well as on initial archive — the metadata survives the worst failure scenario, format-native.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wiring It Up: What a Partial Read Looks Like End-to-End
&lt;/h2&gt;

&lt;p&gt;A data engineer's dbt model lands at the StreamGate HTTP gateway with a &lt;code&gt;Range: bytes=536870912-671088640&lt;/code&gt; request (512 MB – 640 MB, pulling a specific row group from a 4 GB Parquet file on S3):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;1. GET http://localhost:8080/v1/stream/warehouse/events/2024-01-15.parquet
   Range: bytes=536870912-671088640

2. Gateway queries object_frames:
   → nearest frame boundary ≤ 536870912 is at uncompressed_offset=536870912
   → compressed_offset=225,978,112 on Volume D (S3:us-east-1)
   → 6 frames needed, compressed total = 56.3 MB

3. Gateway fires:
   GET s3://huskhoard-cold/volume-d.img
   Range: bytes=225978112-285884415

4. Gateway decompresses frames on the fly, streams bytes 536870912–671088640
   to the client.

5. Total egress from S3: 56.3 MB
   Total egress if client had fetched the full file: 1.71 GB
   Savings: 96.7%
   Time to first byte (LAN): ~180ms vs ~14s for full-file download
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client — dbt, Spark, DuckDB, &lt;code&gt;curl&lt;/code&gt;, whatever — receives a standard HTTP 206 Partial Content response. No special client library. No SDK. Just the HTTP Range spec, universally supported.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Takeaways for Data Engineers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Frame size is a tuning knob.&lt;/strong&gt; HuskHoard defaults to 16 MB frames, optimized for cloud PUT cost (fewer, larger requests) and Zstd compression ratio. For workloads with very fine-grained access patterns (column-level reads in narrow schemas), smaller frames (1–4 MB) reduce the minimum fetch size at the cost of more catalog rows and higher PUT count. Benchmark against your actual access patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. TLV statistics are opt-in per file type.&lt;/strong&gt; For video files you probably don't store column min/max values. For Parquet, CSV, and Arrow IPC files it's worth paying the archiver CPU time to extract and embed statistics at archive time — you pay once and recoup every time a catalog query avoids a volume read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The catalog query is your explain plan.&lt;/strong&gt; Before a restore or a scan, &lt;code&gt;husk catalog query --path "/warehouse/events/*.parquet" --filter "parquet.col.event_ts.min &amp;gt;= 2024-01-01"&lt;/code&gt; shows you which volumes and frame ranges will be touched. Run it first. If the egress estimate is unexpected, the TLV coverage on those files probably needs improving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Multi-volume means cross-volume pruning is free.&lt;/strong&gt; A query that touches two volumes and skips three is doing volume-level predicate pushdown before any I/O. The catalog does this automatically based on the &lt;code&gt;tape_uuid&lt;/code&gt; in each matching row.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Egress savings compound with replication.&lt;/strong&gt; HuskHoard replicates to multiple volumes simultaneously. If your primary volume is on S3 ($0.09/GB egress) and your replica is on Cloudflare R2 ($0.00 egress), the gateway can route the range request to whichever backend minimizes cost. Partial reads from R2 are free. You still benefit from the jump table because API call count and latency still matter.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://huskhoard.com/blog-post-cat.html" rel="noopener noreferrer"&gt;The Catalog Is the Archive: Inside HuskHoard's Ground Truth Engine&lt;/a&gt; — deep dive on the SQLite schema, WAL mode, and catalog rebuild&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://huskhoard.com/blog-post-tag.html" rel="noopener noreferrer"&gt;Data Without Context is Entropy: The Architecture of Tagging&lt;/a&gt; — TLV byte packing, POSIX xattrs, and the self-healing archive&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/HuskHoard/HuskHoard" rel="noopener noreferrer"&gt;HuskHoard on GitHub&lt;/a&gt; — source, README, and the &lt;code&gt;object_frames&lt;/code&gt; implementation in &lt;code&gt;src/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/facebook/zstd/blob/dev/contrib/seekable_format/zstd_seekable_compression_format.md" rel="noopener noreferrer"&gt;Zstd Seekable Format spec&lt;/a&gt; — the upstream spec HuskHoard's frame model is compatible with&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://huskhoard.com/blog-post-parquet.html" rel="noopener noreferrer"&gt;Apache Parquet with an Archive&lt;/a&gt; — Building a Zero-Impact Data Lake&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;HuskHoard is open-source under AGPL v3. If you're running cold data tiers on Linux and want to stop paying for bytes you never read, contributions and issues are welcome at &lt;a href="https://github.com/HuskHoard/HuskHoard" rel="noopener noreferrer"&gt;github.com/HuskHoard/HuskHoard&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>rust</category>
      <category>aws</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title># Why I Bypassed FUSE: Building a Transparent DataTiering Engine in Rust</title>
      <dc:creator>Joichiro Mitaka</dc:creator>
      <pubDate>Fri, 19 Jun 2026 05:51:33 +0000</pubDate>
      <link>https://dev.to/coldstorage/-why-i-bypassed-fuse-building-a-transparent-datatiering-engine-in-rust-4b8d</link>
      <guid>https://dev.to/coldstorage/-why-i-bypassed-fuse-building-a-transparent-datatiering-engine-in-rust-4b8d</guid>
      <description>&lt;p&gt;If you run a home lab or manage large datasets, you’ve hit this wall: NVMe drives are fast but too expensive to hoard data on. Hard drives or cloud buckets are cheap, but they are slow and a pain to manage manually.&lt;/p&gt;

&lt;p&gt;The enterprise world solves this with &lt;strong&gt;HSM Hierarchical Storage Management&lt;/strong&gt; automatically shuffling cold data to slow storage while keeping a transparent stub on the fast drive. But enterprise HSMs cost thousands of dollars and lock your data in proprietary black boxes.&lt;/p&gt;

&lt;p&gt;I wanted this for Linux, for free. So, I started building &lt;strong&gt;HuskHoard&lt;/strong&gt;, an opensource data tiering engine. &lt;/p&gt;

&lt;p&gt;My first thought, like almost every Linux developer building a virtual filesystem, was to use FUSE. But I quickly realized FUSE was the wrong tool for the job. Here is why I abandoned it, and how I used the Linux fanotify API and Rust to build a transparent, zero overhead archiving engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with FUSE
&lt;/h2&gt;

&lt;p&gt;FUSE is fantastic for creating custom filesystems like SSHFS or mounting an S3 bucket. But for an HSM, it creates a massive bottleneck.&lt;/p&gt;

&lt;p&gt;When you use FUSE, &lt;em&gt;every single read and write&lt;/em&gt; has to go through a context switch:&lt;br&gt;
Application &amp;gt; Kernel &amp;gt; FUSE Daemon Userspace &amp;gt; Kernel &amp;gt; Physical Drive.&lt;/p&gt;

&lt;p&gt;If 90% of your data is Hot actively being used on your fast NVMe, forcing it through FUSE overhead completely defeats the purpose of buying expensive NVMe drives in the first place. You sacrifice native I/O performance just to manage the 10% of Cold data.&lt;/p&gt;

&lt;p&gt;I needed a solution where the Hot data ran at native speed, touching nothing but the XFS/Ext4 kernel drivers. &lt;/p&gt;
&lt;h2&gt;
  
  
  The Solution: Enter fanotify
&lt;/h2&gt;

&lt;p&gt;Instead of intercepting every transaction via FUSE, I realized I only needed to intervene in one specific scenario: &lt;strong&gt;When a user tries to open a file that has been archived.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Linux has a kernel API called fanotify originally designed for antivirus scanners. It allows a userspace program to monitor a mount point and, crucially, &lt;em&gt;block&lt;/em&gt; an application from opening a file until the daemon says it’s okay.&lt;/p&gt;

&lt;p&gt;Here is how HuskHoard uses fanotify to create transparent tiering:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Janitor:&lt;/strong&gt; A background Rust thread scans my NVMe drive. When it finds a file that hasnt been touched in 30 days, it compresses it Zstd and moves the payload to a cheap HDD, LTO Tape, or S3 bucket.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Husk Stub:&lt;/strong&gt; It leaves the original file on the NVMe drive but truncates its allocated size to 0 bytes creating a sparse file. To the OS and the user, the file still looks like it’s 50GB and sits in /home/movies.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Interceptor:&lt;/strong&gt; This is where fanotify shines. The HuskHoard daemon listens for FAN_ACCESS_PERM events. If VLC media player tries to open that Husk file, fanotify pauses VLCs execution in the kernel.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Recall:&lt;/strong&gt; HuskHoard intercepts the request, streams the 50GB payload from the tape/S3 bucket back into the sparse file on the NVMe, and then tells fanotify to allow VLC to proceed. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;VLC thinks it just opened a local file. It has no idea the data was fetched from an S3 bucket 50 milliseconds ago. &lt;/p&gt;
&lt;h2&gt;
  
  
  The Rust Implementation
&lt;/h2&gt;

&lt;p&gt;Rust was the obvious choice for this. When you are blocking kernellevel I/O requests, memory safety and predictable latency are nonnegotiable. &lt;/p&gt;

&lt;p&gt;Handling the fanotify loop requires a few specific Linux capabilities specifically CAP_SYS_ADMIN, but Rust allows us to safely manage the multithreaded heavy lifting of the Archive Worker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pub fn run_interceptor&lt;span class="o"&gt;(&lt;/span&gt;config: Arc&amp;lt;HuskConfig&amp;gt;, use_direct_io: bool&lt;span class="o"&gt;)&lt;/span&gt; -&amp;gt; std::io::Result&amp;lt;&lt;span class="o"&gt;()&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;let &lt;/span&gt;watch_dir &lt;span class="o"&gt;=&lt;/span&gt; &amp;amp;config.hot_tier&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;let &lt;/span&gt;db_path &lt;span class="o"&gt;=&lt;/span&gt; &amp;amp;config.db_path&lt;span class="p"&gt;;&lt;/span&gt;
    info!&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;[Daemon] Starting fanotify interceptor on '{}'..."&lt;/span&gt;, watch_dir&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;let &lt;/span&gt;abs_dir &lt;span class="o"&gt;=&lt;/span&gt; std::fs::canonicalize&lt;span class="o"&gt;(&lt;/span&gt;watch_dir&lt;span class="o"&gt;)&lt;/span&gt;?&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nb"&gt;let &lt;/span&gt;fan_fd &lt;span class="o"&gt;=&lt;/span&gt; unsafe &lt;span class="o"&gt;{&lt;/span&gt;
        libc::fanotify_init&lt;span class="o"&gt;(&lt;/span&gt;libc::FAN_CLASS_PRE_CONTENT, libc::O_RDWR as u32&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;fan_fd &amp;lt; 0 &lt;span class="o"&gt;{&lt;/span&gt; 
        &lt;span class="nb"&gt;let &lt;/span&gt;err &lt;span class="o"&gt;=&lt;/span&gt; std::io::Error::last_os_error&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        error!&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" fanotify_init failed: {}. Missing Root or Capabilities!"&lt;/span&gt;, err&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;Err&lt;span class="o"&gt;(&lt;/span&gt;err&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="o"&gt;}&lt;/span&gt;


        &lt;span class="nb"&gt;let &lt;/span&gt;mark_mask &lt;span class="o"&gt;=&lt;/span&gt; libc::FAN_ACCESS_PERM | libc::FAN_CLOSE_WRITE | libc::FAN_EVENT_ON_CHILD&lt;span class="p"&gt;;&lt;/span&gt;

        // 1. Recursively mark the root watch directory and all current subdirectories
        info!&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"[Daemon]  Scanning and attaching listeners to all subdirectories..."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        mark_directory_recursive&lt;span class="o"&gt;(&lt;/span&gt;fan_fd, &amp;amp;abs_dir, mark_mask, &amp;amp;config&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Escaping Vendor Lockin The Easy Exit Promise
&lt;/h2&gt;

&lt;p&gt;One of the biggest issues with commercial HSMs is that if the daemon dies, your data is gone, trapped in proprietary metadata.&lt;/p&gt;

&lt;p&gt;Because I was building this for the opensource community, I enforced a strict Easy Exit architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Payload data is stored in standard &lt;strong&gt;Zstd&lt;/strong&gt; streams verified by &lt;strong&gt;BLAKE3&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  The catalog metadata the Brain tracking where the cold bytes live is an SQLite database.&lt;/li&gt;
&lt;li&gt;  You can natively export the entire catalog to &lt;strong&gt;Apache Parquet&lt;/strong&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means if you decide to stop using HuskHoard, you dont need my software to get your data back. You can query your catalog with DuckDB or Python and manually extract your Zstd archives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whats Next?
&lt;/h2&gt;

&lt;p&gt;Building HuskHoard has been a massive deepdive into Linux kernel APIs and SCSI Tape drivers. Yes, it natively supports physical LTO drives via /dev/nstX to prevent tape shoeshining. &lt;/p&gt;

&lt;p&gt;The engine currently supports automated replication across local drives, tapes, and rclone supported cloud buckets.&lt;/p&gt;

&lt;p&gt;If you are a Rust developer, a HomeLab data hoarder, or just interested in Linux storage architecture, Id love your feedback or code reviews. It is fully AGPL v3 licensed. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check out the repo here:&lt;/strong&gt; [GitHub  HuskHoard]&lt;a href="https://github.com/huskhoard/huskhoard" rel="noopener noreferrer"&gt;https://github.com/huskhoard/huskhoard&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;More architecture details:&lt;/strong&gt; [HuskHoard Blog]&lt;a href="https://www.huskhoard.com/blog.html" rel="noopener noreferrer"&gt;https://www.huskhoard.com/blog.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>rust</category>
      <category>infrastructure</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
