{"id":22801690,"url":"https://github.com/pojntfx/stfs","last_synced_at":"2025-08-21T02:30:43.327Z","repository":{"id":49218955,"uuid":"427117673","full_name":"pojntfx/stfs","owner":"pojntfx","description":"Simple Tape File System (STFS), a file system for tapes and tar files.","archived":false,"fork":false,"pushed_at":"2024-07-30T23:09:05.000Z","size":834,"stargazers_count":57,"open_issues_count":6,"forks_count":2,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-04-08T01:42:28.862Z","etag":null,"topics":["filesystem","ltfs","sqlite","tape","tape-archive","tape-drive","tape-library","tar"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pojntfx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-11T19:24:09.000Z","updated_at":"2025-04-07T19:47:10.000Z","dependencies_parsed_at":"2024-07-31T02:21:58.231Z","dependency_job_id":null,"html_url":"https://github.com/pojntfx/stfs","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/pojntfx/stfs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pojntfx%2Fstfs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pojntfx%2Fstfs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pojntfx%2Fstfs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pojntfx%2Fstfs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pojntfx","download_url":"https://codeload.github.com/pojntfx/stfs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pojntfx%2Fstfs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271416643,"owners_count":24755921,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-21T02:00:08.990Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["filesystem","ltfs","sqlite","tape","tape-archive","tape-drive","tape-library","tar"],"created_at":"2024-12-12T08:12:26.852Z","updated_at":"2025-08-21T02:30:42.906Z","avatar_url":"https://github.com/pojntfx.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# STFS\n\nSimple Tape File System (STFS), a file system for tapes and tar files.\n\n[![hydrun CI](https://github.com/pojntfx/stfs/actions/workflows/hydrun.yaml/badge.svg)](https://github.com/pojntfx/stfs/actions/workflows/hydrun.yaml)\n![Go Version](https://img.shields.io/badge/go%20version-%3E=1.21-61CFDD.svg)\n[![Go Reference](https://pkg.go.dev/badge/github.com/pojntfx/stfs.svg)](https://pkg.go.dev/github.com/pojntfx/stfs)\n[![Matrix](https://img.shields.io/matrix/stfs:matrix.org)](https://matrix.to/#/#stfs:matrix.org?via=matrix.org)\n\n## Overview\n\nSTFS is a filesystem that brings tapes and tar files into the 21st century.\n\nIt enables you to:\n\n- **Use a tape or tar file like a regular disk**: STFS uses the robust `tar` format and tape technology to provide a fully features filesystem. This makes such storage solutions much for accessible and manageable, while also significantly increasing the performance of everyday operations such as listing and searching for files by using a SQLite-based on-disk index.\n- **Archive data securely**: The integrated transparent, asymmetrical encryption and signature support makes it possible to use almost any tape as a regulations compliant storage medium, while still being able to take advantage of all the benefits of tapes like reduced cost and increased reliability.\n- **Compress data efficiently**: By leveraging the embedded compression functionality, it is possible to store even more data on tapes without sacrificing the user experience.\n- **Recover data in unexpected scenarios**: Even if sudden power drops happen, the drive fails, the index gets corrupted or STFS stops being maintained, your data is secure. Because it is based on open standards such as `tar`, SQLite, `zstandard`, PGP and others, it is possible to extract your data even if STFS's integrated recovery tools don't suffice.\n- **Build your own storage solution**: In addition to its own, optimized APIs, STFS provides a [`afero.FS implementation`](https://github.com/spf13/afero). This makes embedding STFS and accessing files on a tape or in a tar file through Go easy.\n\n## Installation\n\nStatic binaries are available on [GitHub releases](https://github.com/pojntfx/stfs/releases).\n\nOn Linux, you can install them like so:\n\n```shell\n$ curl -L -o /tmp/stfs \"https://github.com/pojntfx/stfs/releases/latest/download/stfs.linux-$(uname -m)\"\n$ sudo install /tmp/stfs /usr/local/bin\n```\n\nOn macOS, you can use the following:\n\n```shell\n$ curl -L -o /tmp/stfs \"https://github.com/pojntfx/stfs/releases/latest/download/stfs.darwin-$(uname -m)\"\n$ sudo install /tmp/stfs /usr/local/bin\n```\n\nOn Windows, the following should work (using PowerShell as administrator):\n\n```shell\nPS\u003e Invoke-WebRequest https://github.com/pojntfx/stfs/releases/latest/download/stfs.windows-x86_64.exe -OutFile \\Windows\\System32\\stfs.exe\n```\n\nNote that only the Linux version supports reading from tape drives; macOS and Windows are limited to operating on tar files.\n\nYou can find binaries for more operating systems and architectures on [GitHub releases](https://github.com/pojntfx/stfs/releases).\n\n## Tutorial\n\n\u003e Please note that this is only a short overview and does not explain all configuration options. To get more info on available commands or options, use `--help`.\n\n### 1. Generating Keys with `stfs keygen`\n\nWhile not strictly required, it is recommended to generate keys to sign and encrypt your data on tape. There are multiple methods available; `PGP` and [age](https://github.com/FiloSottile/age) for encryption, and `PGP` as well as [Minisign](https://github.com/aead/minisign) for signatures. In most cases, using age for encryption and PGP for signatures is the best option. To generate the appropriate keys, run the following; make sure to save the keys in a secure location and use a secure password:\n\n```shell\n$ stfs keygen --encryption age --password mysecureencryptionpassword --identity ~/.stfs-age.priv --recipient ~/.stfs-age.pub\n$ stfs keygen --signature pgp --password mysecuresignaturepassword --identity ~/.stfs-pgp.priv --recipient ~/.stfs-pgp.pub\n```\n\nFor more information, see the [key generation reference](#key-generation).\n\n### 2. Serving a Tape Read-Write with `stfs serve ftp`\n\nThe simplest way to read or write to/from the tape (or tar file) is to use the integrated FTP server. To speed up operations, caching mechanisms and compression are available. For the write cache (`--cache-write-type`) the following types are available:\n\n- `memory`: A simple in-memory cache; should not be used in most cases due to potential RAM exhaustion when adding large files\n- `file`: A on-disk cache; this is recommended in most cases, especially if a SSD is available\n\nFor the read cache (`--cache-filesystem-type`), which is especially useful when working with many small files, similar types are available (`memory` and `dir`). `dir` uses a overlay filesystem to cache files in the directory specified with `--cache-dir`.\n\nTo further speed up IO-limited read/write operations, multiple compression options are available to be selected with `--compression` and can be tuned with `--compression-level`:\n\n- `zstandard`: A Meta-led replacement for `gzip` with very high speeds and a very good compression ratio; this is recommended for most users\n- `gzip`/`parallelgzip`: The GNU format commonly used in combination with `tar`, i.e. for `.tar.gz`; reasonably fast and with a good compression ratio\n- `bzip2`/`parallelbzip2`: A reliable compression format with good speeds and a better compression ratio than `gzip`.\n- `lz4`: Very fast, but at the cost of a lower compression ratio\n- `brotli`: A Google-led compression format with good adoption on the web platform; very high compression ratio, very slow speeds\n\nTo serve a tape (or tar file), run the following (adjust the options accordingly):\n\n```shell\n# Use `-d /dev/nst0` for your primary tape drive instead\n$ stfs serve ftp \\\n    -d ~/Downloads/drive.tar \\\n    -m ~/Downloads/metadata.sqlite \\\n    -e age \\\n    --encryption-identity ~/.stfs-age.priv \\\n    --encryption-recipient ~/.stfs-age.pub \\\n    --encryption-password mysecureencryptionpassword \\\n    -s pgp \\\n    --signature-identity ~/.stfs-pgp.priv \\\n    --signature-recipient ~/.stfs-pgp.pub \\\n    --signature-password mysecuresignaturepassword \\\n    --compression zstandard\n{\"time\":1652646259,\"level\":\"INFO\",\"event\":\"FTP server listening\",\"data\":[{\"laddr\":\":1337\"}]}\n{\"time\":1652646259,\"level\":\"INFO\",\"event\":\"Listening...\",\"data\":[\"address\",{\"IP\":\"::\",\"Port\":1337,\"Zone\":\"\"}]}\n{\"time\":1652646259,\"level\":\"INFO\",\"event\":\"Starting...\",\"data\":null}\n```\n\nYou can now point your file manager (GNOME files on Linux, Windows Explorer on Windows and Finder on macOS all have support for it, but macOS is read-only) to `ftp://localhost:1337` and read/write files from the tape (or tape file).\n\nFor more information, see the [servers reference](#servers).\n\n### 3. Serving a Tape Read-Only with `stfs serve http`\n\nIf you want to serve a tape (or tar file) read-only, using the integrated HTTP server is the best option. It inherits all the same options from [Serving a Tape Read-Write with `stfs serve ftp`](#2-serving-a-tape-read-write-with-stfs-serve-ftp), minus the write cache due to it being read-only. To use it, run:\n\n```shell\n# Use `-d /dev/nst0` for your primary tape drive instead\n$ stfs serve http \\\n    -d ~/Downloads/drive.tar \\\n    -m ~/Downloads/metadata.sqlite \\\n    -e age \\\n    --identity ~/.stfs-age.priv \\\n    --password mysecureencryptionpassword \\\n    -s pgp \\\n    --recipient ~/.stfs-pgp.pub \\\n    --compression zstandard\n{\"time\":1652653259,\"level\":\"INFO\",\"event\":\"HTTP server listening\",\"data\":[{\"laddr\":\":1337\"}]}\n```\n\nYou can now point your web browser to `http://localhost:1337` and read files from the tape (or tape file).\n\nFor more information, see the [servers reference](#servers).\n\n### 4. Using Optimized Operations with `stfs operation`\n\nWhile the file system API is convenient because of its similarity to most filesystems, it also can't be used without a write cache. While this isn't an issue for most applications, it requires you to have a disk that is at least as large as the largest file you want to add to the tape. To get around these limitations, STFS also provides a `tar`-like interface for interacting with the tape. Please note that these operations should be used carefully, as the usual checks (such as checking if a parent directory exists before adding files to it) don't apply.\n\nFirst, initialize an empty tape:\n\n```shell\n# Use `-d /dev/nst0` for your primary tape drive instead\n$ stfs operation initialize \\\n    -d ~/Downloads/drive.tar \\\n    -m ~/Downloads/metadata.sqlite \\\n    -e age \\\n    --recipient ~/.stfs-age.pub \\\n    -s pgp \\\n    --identity ~/.stfs-pgp.priv \\\n    --password mysecuresignaturepassword \\\n    --compression zstandard\ntype,indexed,record,lastknownrecord,block,lastknownblock,typeflag,name,linkname,size,mode,uid,gid,uname,gname,modtime,accesstime,changetime,devmajor,devminor,paxrecords,format\narchive,false,-1,-1,-1,-1,53,/,,0,511,1000,1000,pojntfx,1000,2022-05-16T22:24:13+02:00,0001-01-01T00:00:00Z,0001-01-01T00:00:00Z,0,0,null,4\narchive,true,0,-1,0,-1,53,/,,0,511,1000,1000,pojntfx,1000,2022-05-16T22:24:13+02:00,0001-01-01T00:00:00Z,0001-01-01T00:00:00Z,0,0,null,4\n```\n\nYou can now add files to it:\n\n```shell\n# Use `-d /dev/nst0` for your primary tape drive instead\n$ stfs operation archive \\\n    -d ~/Downloads/drive.tar \\\n    -m ~/Downloads/metadata.sqlite \\\n    -e age \\\n    --recipient ~/.stfs-age.pub \\\n    -s pgp \\\n    --identity ~/.stfs-pgp.priv \\\n    --password mysecuresignaturepassword \\\n    --compression zstandard \\\n    .\n# ...\narchive,true,1480,-1,9,-1,48,pkg/tape/write.go,,1544,420,1000,1000,pojntfx,pojntfx,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,0,0,\"{\"\"STFS.Signature\"\":\"\"wnUEABYIACcFAmKCsyUJkGA0c/4XcV5qFqEEjWKRLHhppJ6S+ZJlYDRz/hdxXmoAAF5mAP95DKo/r136fL/SKuBwmxoMNfGZ+v61bwk/xcOBQk5vrwEAs07QV2RF6h/FME+/nXxjZrbBWmFWg8pC4IGdScnJbQ4=\"\",\"\"STFS.UncompressedSize\"\":\"\"1544\"\"}\",4\narchive,true,1480,-1,17,-1,53,pkg/utility,,0,509,1000,1000,pojntfx,pojntfx,2022-04-18T20:19:52+02:00,2022-05-15T23:36:33+02:00,2022-04-23T16:08:59+02:00,0,0,null,4\n# ...\n```\n\nFull CRUD support is implemented, so you can `delete`, `move`, `restore` and `update` files like this as well. For example, to restore `pkg/tape/write.go`, run the following:\n\n```shell\n# Use `-d /dev/nst0` for your primary tape drive instead\n$ stfs operation restore \\\n    -d ~/Downloads/drive.tar \\\n    -m ~/Downloads/metadata.sqlite \\\n    -e age \\\n    --identity ~/.stfs-age.priv \\\n    --password mysecureencryptionpassword \\\n    -s pgp \\\n    --recipient ~/.stfs-pgp.pub \\\n    --compression zstandard \\\n    --flatten \\\n    --from pkg/tape/write.go \\\n    --to write.go\ntype,indexed,record,lastknownrecord,block,lastknownblock,typeflag,name,linkname,size,mode,uid,gid,uname,gname,modtime,accesstime,changetime,devmajor,devminor,paxrecords,format\nrestore,true,1480,1480,9,9,48,/pkg/tape/write.go,,1544,420,1000,1000,pojntfx,pojntfx,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,0,0,\"{\"\"STFS.Signature\"\":\"\"wnUEABYIACcFAmKCsyUJkGA0c/4XcV5qFqEEjWKRLHhppJ6S+ZJlYDRz/hdxXmoAAF5mAP95DKo/r136fL/SKuBwmxoMNfGZ+v61bwk/xcOBQk5vrwEAs07QV2RF6h/FME+/nXxjZrbBWmFWg8pC4IGdScnJbQ4=\"\",\"\"STFS.UncompressedSize\"\":\"\"1544\"\"}\",4\n$ file write.go\nwrite.go: ASCII text\n```\n\nFor more information, see the [operations reference](#operations).\n\n### 5. Managing the Index with `stfs inventory`\n\nFor similar reasons as described in [Using Optimized Operations with `stfs operation`](#4-using-optimized-operations-with-stfs-operation), it can make sense to take advantage of the index in order to quickly find a file or directory. For example, to list all files in the `pkg` directory, run the following:\n\n```shell\n$ stfs inventory list \\\n    -m ~/Downloads/metadata.sqlite \\\n    --name pkg\nrecord,lastknownrecord,block,lastknownblock,typeflag,name,linkname,size,mode,uid,gid,uname,gname,modtime,accesstime,changetime,devmajor,devminor,paxrecords,format\n1454,1454,14,14,53,/pkg/cache,,0,493,1000,1000,pojntfx,pojntfx,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,0,0,null,4\n# ...\n```\n\nIt is also possible to search for a file by regex with `stfs inventory find`:\n\n```shell\n$ stfs inventory find \\\n    -m ~/Downloads/metadata.sqlite \\\n    --expression '(.*).yaml'\nrecord,lastknownrecord,block,lastknownblock,typeflag,name,linkname,size,mode,uid,gid,uname,gname,modtime,accesstime,changetime,devmajor,devminor,paxrecords,format\n118,118,11,11,48,/.github/workflows/hydrun.yaml,,3000,420,1000,1000,pojntfx,pojntfx,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,0,0,\"{\"\"STFS.Signature\"\":\"\"wnUEABYIACcFAmKCsyMJkGA0c/4XcV5qFqEEjWKRLHhppJ6S+ZJlYDRz/hdxXmoAAF9nAP98fVspytLtKjTATbtH7hoVaK7tyVGKVDabY4OkOnYiygD/QcTtdWR48Eq5pIT0bR2M9u168aXTbWoWX8JVXcm7uwg=\"\",\"\"STFS.UncompressedSize\"\":\"\"3000\"\"}\",4\n# ...\n```\n\nIt is also possible to get information on a single file or directory using `stfs inventory stat`. For more information, see the [inventory reference](#inventory).\n\n### 6. Recovering Data with `stfs recovery`\n\nIn case of unfinished write operations, sudden power losses or other forms of data corruption, the integrated recovery tools can help. For example, to query a tape starting from a specific record and block, use `stfs query`:\n\n```shell\n# Use `-d /dev/nst0` for your primary tape drive instead\n$ stfs recovery query \\\n    -d ~/Downloads/drive.tar \\\n    -e age \\\n    --identity ~/.stfs-age.priv \\\n    --password mysecureencryptionpassword \\\n    -s pgp \\\n    --recipient ~/.stfs-pgp.pub \\\n    --compression zstandard \\\n    --record 118 \\\n    --block 11\n118,-1,11,-1,48,.github/workflows/hydrun.yaml.zst.age,,1272,420,1000,1000,pojntfx,pojntfx,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,0,0,\"{\"\"STFS.Signature\"\":\"\"wnUEABYIACcFAmKCsyMJkGA0c/4XcV5qFqEEjWKRLHhppJ6S+ZJlYDRz/hdxXmoAAF9nAP98fVspytLtKjTATbtH7hoVaK7tyVGKVDabY4OkOnYiygD/QcTtdWR48Eq5pIT0bR2M9u168aXTbWoWX8JVXcm7uwg=\"\",\"\"STFS.UncompressedSize\"\":\"\"3000\"\"}\",4\n119,-1,0,-1,48,.gitignore.zst.age,,220,436,1000,1000,pojntfx,pojntfx,2022-04-18T20:19:52+02:00,2022-05-15T23:38:41+02:00,2022-04-23T16:08:59+02:00,0,0,\"{\"\"STFS.Signature\"\":\"\"wnUEABYIACcFAmKCsyMJkGA0c/4XcV5qFqEEjWKRLHhppJ6S+ZJlYDRz/hdxXmoAAGAmAP9G6z9HSr5puQjDMRpYZ11Jge95wG2g3LSetF+ts4CG7wEA38qbJx92BbQN4tWmm5G3dXg+PAnGKONAkc0IU9dmtgA=\"\",\"\"STFS.UncompressedSize\"\":\"\"4\"\"}\",4\n# ...\n```\n\nIf you know the record and block of a file (which you can get from the index with the `inventory` commands), you can also recover it directly:\n\n```shell\n# Use `-d /dev/nst0` for your primary tape drive instead\n$ stfs recovery fetch \\\n    -d ~/Downloads/drive.tar \\\n    -e age \\\n    --identity ~/.stfs-age.priv \\\n    --password mysecureencryptionpassword \\\n    -s pgp \\\n    --recipient ~/.stfs-pgp.pub \\\n    --compression zstandard \\\n    --record 118 \\\n    --block 11 \\\n    --to hydrun.yaml\nrecord,lastknownrecord,block,lastknownblock,typeflag,name,linkname,size,mode,uid,gid,uname,gname,modtime,accesstime,changetime,devmajor,devminor,paxrecords,format\n118,-1,11,-1,48,.github/workflows/hydrun.yaml.zst.age,,1272,420,1000,1000,pojntfx,pojntfx,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,2022-05-15T23:41:54+02:00,0,0,\"{\"\"STFS.Signature\"\":\"\"wnUEABYIACcFAmKCsyMJkGA0c/4XcV5qFqEEjWKRLHhppJ6S+ZJlYDRz/hdxXmoAAF9nAP98fVspytLtKjTATbtH7hoVaK7tyVGKVDabY4OkOnYiygD/QcTtdWR48Eq5pIT0bR2M9u168aXTbWoWX8JVXcm7uwg=\"\",\"\"STFS.UncompressedSize\"\":\"\"3000\"\"}\",4\n$ file hydrun.yaml\nhydrun.yaml: ASCII text\n```\n\nIt is also possible to restore a broken index from scratch with `stfs recovery index`. For more information, see the [recovery reference](#recovery).\n\n### 7. Managing the Drive with `stfs drive`\n\nSTFS can also manage the physical tape drive directly without having to rely on external tools. For example, to eject a tape from the drive, use `stfs drive eject`:\n\n```shell\n$ stfs drive eject \\\n    -d /dev/nst0\n```\n\nIt is also possible to get the current tape position with `stfs drive tell`. For more information, see the [drive management reference](#drive-management).\n\n### 8. Embedding STFS with `fs.STFS`\n\nSTFS at its core provides quite a few public APIs, but the easiest way to embed it is to use it's provided [`afero.FS implementation`](https://github.com/spf13/afero). This makes it possible to easily swap out the filesystem implementation with a native one, layer caching implementations and decouple your storage layer.\n\nUsing this API is fairly straightforward:\n\n```go\n// ...\nstfs := fs.NewSTFS(\n\treadOps,\n\twriteOps,\n\n\tconfig.MetadataConfig{\n\t\tMetadata: metadataPersister,\n\t},\n\n\tconfig.CompressionLevelFastestKey,\n\tfunc() (cache.WriteCache, func() error, error) {\n\t\treturn cache.NewCacheWrite(\n\t\t\t*writeCacheFlag,\n\t\t\tconfig.WriteCacheTypeFile,\n\t\t)\n\t},\n\tfalse,\n\tfalse,\n\n\tfunc(hdr *config.Header) {\n\t\tl.Trace(\"Header transform\", hdr)\n\t},\n\tl,\n)\n\nroot, err := stfs.Initialize(\"/\", os.ModePerm)\nif err != nil {\n\tpanic(err)\n}\n\nfs, err := cache.NewCacheFilesystem(\n\tstfs,\n\troot,\n\tconfig.NoneKey,\n\t0,\n\t\"\",\n)\nif err != nil {\n\tpanic(err)\n}\n```\n\nYou can now use the Afero APIs to interact with the filesystem; if you've worked with Go's `fs` package before, they should be very familiar:\n\n```go\nlog.Println(\"stat /\")\n\nstat, err := fs.Stat(\"/\")\nif err != nil {\n\tpanic(err)\n}\n\nlog.Println(\"Result of stat /:\", stat)\n\nlog.Println(\"create /test.txt\")\n\nfile, err := fs.Create(\"/test.txt\")\nif err != nil {\n\tpanic(err)\n}\n\nlog.Println(\"Result of create /test.txt:\", file)\n\nlog.Println(\"writeString /test.txt\")\n\nn, err := file.WriteString(\"Hello, world!\")\nif err != nil {\n\tpanic(err)\n}\n\nlog.Println(\"Result of writeString /test.txt:\", n)\n\nif err := file.Close(); err != nil {\n\tpanic(err)\n}\n\n// ...\n```\n\nNote that STFS also implements `afero.Symlinker`, so symlinks are available as well.\n\nFor more information, check out the [Go API](https://pkg.go.dev/github.com/pojntfx/stfs) and take a look at the provided [examples](./examples), utilities, services and tests in the package for examples.\n\n🚀 **That's it!** We hope you enjoy using STFS.\n\n## Reference\n\n### Command Line Arguments\n\n```shell\n$ stfs --help\nSimple Tape File System (STFS), a file system for tapes and tar files.\n\nFind more information at:\nhttps://github.com/pojntfx/stfs\n\nUsage:\n  stfs [command]\n\nAvailable Commands:\n  completion  Generate the autocompletion script for the specified shell\n  drive       Manage tape drives\n  help        Help about any command\n  inventory   Get contents and metadata of tape or tar file from the index\n  keygen      Generate a encryption or signature key\n  operation   Perform operations on tape or tar file and the index\n  recovery    Recover tapes or tar files\n  serve       Serve tape or tar file and the index\n\nFlags:\n  -c, --compression string   Compression format to use (default , available are [ gzip parallelgzip lz4 zstandard brotli bzip2 parallelbzip2])\n  -d, --drive string         Tape or tar file to use (default \"/dev/nst0\")\n  -e, --encryption string    Encryption format to use (default , available are [ age pgp])\n  -h, --help                 help for stfs\n  -m, --metadata string      Metadata database to use (default \"/home/pojntfx/.local/share/stfs/var/lib/stfs/metadata.sqlite\")\n  -s, --signature string     Signature format to use (default , available are [ minisign pgp])\n  -v, --verbose int          Verbosity level (default 2, available are [0 1 2 3 4]) (default 2)\n\nUse \"stfs [command] --help\" for more information about a command.\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eExpand subcommand reference\u003c/summary\u003e\n\n#### Drive Management\n\n```shell\n$ stfs drive --help\nManage tape drives\n\nUsage:\n  stfs drive [command]\n\nAliases:\n  drive, dri, d\n\nAvailable Commands:\n  eject       Eject tape from drive\n  tell        Get the current record on the tape\n\nFlags:\n  -h, --help   help for drive\n\nGlobal Flags:\n  -c, --compression string   Compression format to use (default , available are [ gzip parallelgzip lz4 zstandard brotli bzip2 parallelbzip2])\n  -d, --drive string         Tape or tar file to use (default \"/dev/nst0\")\n  -e, --encryption string    Encryption format to use (default , available are [ age pgp])\n  -m, --metadata string      Metadata database to use (default \"/home/pojntfx/.local/share/stfs/var/lib/stfs/metadata.sqlite\")\n  -s, --signature string     Signature format to use (default , available are [ minisign pgp])\n  -v, --verbose int          Verbosity level (default 2, available are [0 1 2 3 4]) (default 2)\n\nUse \"stfs drive [command] --help\" for more information about a command.\n```\n\n#### Inventory\n\n```shell\n$ stfs inventory --help\nGet contents and metadata of tape or tar file from the index\n\nUsage:\n  stfs inventory [command]\n\nAliases:\n  inventory, inv, i\n\nAvailable Commands:\n  find        Find a file or directory on tape or tar file by matching against a regex\n  list        List the contents of a directory on tape or tar file\n  stat        Get information on a file or directory on tape or tar file\n\nFlags:\n  -h, --help   help for inventory\n\nGlobal Flags:\n  -c, --compression string   Compression format to use (default , available are [ gzip parallelgzip lz4 zstandard brotli bzip2 parallelbzip2])\n  -d, --drive string         Tape or tar file to use (default \"/dev/nst0\")\n  -e, --encryption string    Encryption format to use (default , available are [ age pgp])\n  -m, --metadata string      Metadata database to use (default \"/home/pojntfx/.local/share/stfs/var/lib/stfs/metadata.sqlite\")\n  -s, --signature string     Signature format to use (default , available are [ minisign pgp])\n  -v, --verbose int          Verbosity level (default 2, available are [0 1 2 3 4]) (default 2)\n\nUse \"stfs inventory [command] --help\" for more information about a command.\n```\n\n#### Key Generation\n\n```shell\n$ stfs keygen --help\nGenerate a encryption or signature key\n\nUsage:\n  stfs keygen [flags]\n\nAliases:\n  keygen, key, k\n\nFlags:\n  -h, --help               help for keygen\n  -i, --identity string    Path to write the private key to\n  -p, --password string    Password to protect the private key with\n  -r, --recipient string   Path to write the public key to\n\nGlobal Flags:\n  -c, --compression string   Compression format to use (default , available are [ gzip parallelgzip lz4 zstandard brotli bzip2 parallelbzip2])\n  -d, --drive string         Tape or tar file to use (default \"/dev/nst0\")\n  -e, --encryption string    Encryption format to use (default , available are [ age pgp])\n  -m, --metadata string      Metadata database to use (default \"/home/pojntfx/.local/share/stfs/var/lib/stfs/metadata.sqlite\")\n  -s, --signature string     Signature format to use (default , available are [ minisign pgp])\n  -v, --verbose int          Verbosity level (default 2, available are [0 1 2 3 4]) (default 2)\n```\n\n#### Operations\n\n```shell\n$ stfs operation --help\nPerform operations on tape or tar file and the index\n\nUsage:\n  stfs operation [command]\n\nAliases:\n  operation, ope, o, op\n\nAvailable Commands:\n  archive     Archive a file or directory to tape or tar file\n  delete      Delete a file or directory from tape or tar file\n  initialize  Truncate and initalize a file or directory\n  move        Move a file or directory on tape or tar file\n  restore     Restore a file or directory from tape or tar file\n  update      Update a file or directory's content and metadata on tape or tar file\n\nFlags:\n  -h, --help   help for operation\n\nGlobal Flags:\n  -c, --compression string   Compression format to use (default , available are [ gzip parallelgzip lz4 zstandard brotli bzip2 parallelbzip2])\n  -d, --drive string         Tape or tar file to use (default \"/dev/nst0\")\n  -e, --encryption string    Encryption format to use (default , available are [ age pgp])\n  -m, --metadata string      Metadata database to use (default \"/home/pojntfx/.local/share/stfs/var/lib/stfs/metadata.sqlite\")\n  -s, --signature string     Signature format to use (default , available are [ minisign pgp])\n  -v, --verbose int          Verbosity level (default 2, available are [0 1 2 3 4]) (default 2)\n\nUse \"stfs operation [command] --help\" for more information about a command.\n```\n\n#### Recovery\n\n```shell\n$ stfs recovery --help\nRecover tapes or tar files\n\nUsage:\n  stfs recovery [command]\n\nAliases:\n  recovery, rec, r\n\nAvailable Commands:\n  fetch       Fetch a file or directory from tape or tar file by record and block without the index\n  index       Index contents of tape or tar file\n  query       Query contents of tape or tar file without the index\n\nFlags:\n  -h, --help   help for recovery\n\nGlobal Flags:\n  -c, --compression string   Compression format to use (default , available are [ gzip parallelgzip lz4 zstandard brotli bzip2 parallelbzip2])\n  -d, --drive string         Tape or tar file to use (default \"/dev/nst0\")\n  -e, --encryption string    Encryption format to use (default , available are [ age pgp])\n  -m, --metadata string      Metadata database to use (default \"/home/pojntfx/.local/share/stfs/var/lib/stfs/metadata.sqlite\")\n  -s, --signature string     Signature format to use (default , available are [ minisign pgp])\n  -v, --verbose int          Verbosity level (default 2, available are [0 1 2 3 4]) (default 2)\n\nUse \"stfs recovery [command] --help\" for more information about a command.\n```\n\n#### Servers\n\n```shell\nstfs serve --help\nServe tape or tar file and the index\n\nUsage:\n  stfs serve [command]\n\nAliases:\n  serve, ser, s, srv\n\nAvailable Commands:\n  ftp         Serve tape or tar file and the index over FTP (read-write)\n  http        Serve tape or tar file and the index over HTTP (read-only)\n\nFlags:\n  -h, --help   help for serve\n\nGlobal Flags:\n  -c, --compression string   Compression format to use (default , available are [ gzip parallelgzip lz4 zstandard brotli bzip2 parallelbzip2])\n  -d, --drive string         Tape or tar file to use (default \"/dev/nst0\")\n  -e, --encryption string    Encryption format to use (default , available are [ age pgp])\n  -m, --metadata string      Metadata database to use (default \"/home/pojntfx/.local/share/stfs/var/lib/stfs/metadata.sqlite\")\n  -s, --signature string     Signature format to use (default , available are [ minisign pgp])\n  -v, --verbose int          Verbosity level (default 2, available are [0 1 2 3 4]) (default 2)\n\nUse \"stfs serve [command] --help\" for more information about a command.\n```\n\n\u003c/details\u003e\n\n### Environment Variables\n\nAll command line arguments described above can also be set using environment variables; for example, to set `--drive` to `/tmp/drive.tar` with an environment variable, use `STFS_DRIVE=/tmp/drive.tar`.\n\n## Acknowledgements\n\n- [aead.dev/minisign](https://github.com/aead/minisign) provides the Minisign signature implementation.\n- [filippo.io/age](https://github.com/FiloSottile/age) provides the Age encryption implementation.\n- [ProtonMail/gopenpgp](github.com/ProtonMail/gopenpgp) provides the PGP signature and encryption implementation.\n- [fclairamb/ftpserverlib](github.com/fclairamb/ftpserverlib) provides the FTP server implementation.\n- [spf13/afero](https://github.com/spf13/afero) provides the file system abstraction layer implementation.\n\n## Contributing\n\nTo contribute, please use the [GitHub flow](https://guides.github.com/introduction/flow/) and follow our [Code of Conduct](./CODE_OF_CONDUCT.md).\n\nTo build and start a development version of STFS locally, run the following:\n\n```shell\n$ git clone https://github.com/pojntfx/stfs.git\n$ cd stfs\n$ make depend\n$ make \u0026\u0026 sudo make install\n$ stfs serve ftp -d /tmp/drive.tar -m /tmp/dev.sqlite # Now point your file explorer to `ftp://localhost:1337`\n```\n\nHave any questions or need help? Chat with us [on Matrix](https://matrix.to/#/#stfs:matrix.org?via=matrix.org)!\n\n## License\n\nSTFS (c) 2024 Felicitas Pojtinger and contributors\n\nSPDX-License-Identifier: AGPL-3.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpojntfx%2Fstfs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpojntfx%2Fstfs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpojntfx%2Fstfs/lists"}