Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use hashes for cache keys #780

Closed
1 task done
Tracked by #781
jonjohnsonjr opened this issue Jul 1, 2023 · 4 comments · Fixed by chainguard-dev/go-apk#77
Closed
1 task done
Tracked by #781

Use hashes for cache keys #780

jonjohnsonjr opened this issue Jul 1, 2023 · 4 comments · Fixed by chainguard-dev/go-apk#77

Comments

@jonjohnsonjr
Copy link
Contributor

jonjohnsonjr commented Jul 1, 2023

IIUC, we currently rely on Etags for both the APKINDEX and individual APKs when checking our cache.

Looking at a trace, you can see we are spending ~8ms on every cache hit just sending a HEAD request to get the etag:

image

This shouldn't be necessary because the APKINDEX is a happy little DAG that has all the information we need.

The C: field in APKINDEX contains the checksum of the APK control section.

The control segment is constructed by placing each file for the package into a tar record, concatenating those tar records, gzipping the tar records, and concatenating them onto the front of the data tarball. The SHA1 hash of this gzip stream is used as the checksum C: field in the APKINDEX file.

The datahash field in the control section's .PKGINFO contains the sha256 of the data section.

datahash - hex-encoded sha256 checksum of the data tarball

(TODO: Is this the compressed bytes or uncompressed bytes?)

(Source: https://wiki.alpinelinux.org/wiki/Apk_spec)

image

The bold edges represent content hashes. Note that we have a chain of them from the APKINDEX all the way to the APK's data section, so we can just use those hashes as keys in our cache to determine if we can reuse an APK.

My initial thought for directory structure would be something like:

cache
├── APKINDEX
│   └── deadbeefdeadbeef.tar.gz
└── foo-1.2.3.apk
    ├── 00fbaddad.dat.tar.gz
    ├── badc0ffee.sig.tar.gz
    └── badc0ffee.ctl.tar.gz

Where under APKINDEX we have etag-based filenames, under each APK version we have the control section named after the checksum and the data section named after the datahash. We can just stat the files with the expected name (and re-hash them as we consume them to prevent tampering) to check existence rather than going all the way out to the internet.

(Splitting the APK like this will make cache validation much easier but also give us a performance benefit.)

Tasks

@jonjohnsonjr
Copy link
Contributor Author

(TODO: Is this the compressed bytes or uncompressed bytes?)

$ cat busybox-1.36.1-r0.apk | tar -Ox .PKGINFO | grep datahash
datahash = 54af314783a54b1585f43b739e1c668233957536d15521572e6885cb33940603

$ cat busybox-1.36.1-r0.apk | tail -c +1155 | shasum -a 256
54af314783a54b1585f43b739e1c668233957536d15521572e6885cb33940603  -

Looks like sha256(compressed).

@jonjohnsonjr
Copy link
Contributor Author

Looking at APKINDEX.tar.gz, we have this entry:

C:Q1KnPBl8qH8bANebdR3xKV2XVgyEk=
P:busybox
V:1.36.1-r0
A:x86_64
S:375966
I:668764
T:swiss-army knife for embedded systems
L:GPL-2.0-only
o:busybox
t:1685128304
c:f1ba2652e71647a8c2a25032a699f701dd95536c
D:so:ld-linux-x86-64.so.2 so:libc.so.6 so:libcrypt.so.1 so:libm.so.6
p:cmd:busybox=1.36.1-r0

The sha1 of the control segment:

cat busybox-1.36.1-r0.apk | tail -c +663 | head -c 492 | shasum -a 1
2a73c197ca87f1b00d79b751df1295d97560c849  -

Matches what's in APKINDEX:

$ echo Q1KnPBl8qH8bANebdR3xKV2XVgyEk= | tail -c +3 | base64 -d | xxd -p
2a73c197ca87f1b00d79b751df1295d97560c849

So this will dovetail really well with #772 because in order to verify these hashes, we need to split the APK into 3 different gzipped segments anyway, so we might as well cache those segments separately with these hashes as their keys. Then we can look up by hash and avoid parsing stuff multiple times.

@jonjohnsonjr
Copy link
Contributor Author

jonjohnsonjr commented Jul 6, 2023

Looking at the state of my current cache:

ls -1 ~/Library/Caches/dev.chainguard.go-apk/https%3A%2F%2Fpackages.wolfi.dev%2Fos/x86_64/
0a7ddce7b13f11750db4e97ee6a354d6.etag
49ced96162e730a2f2bb5d329a62bcaf.etag
54e8d8ff2530eee95b37d93ddf579166.etag
82214f25069eb8ed5b31a0d8ac6024f8.etag
945ca68e5321ca4c53c28394a66b6d56.etag
9adc71670338247bae00fb6484c9339b.etag
a356b955a5104da3c330146abb11d050.etag
bash-5.2.15-r2.apk
binutils-2.40-r3.apk
binutils-gold-2.40-r3.apk
build-base-1-r5.apk
busybox-1.36.1-r0.apk
busybox-1.36.1-r2.apk
ca-certificates-bundle-20230506-r0.apk
ce1d70c03338934c1254b6a1baed55d5.etag
expat-2.5.0-r3.apk
gcc-13.1.0-r1.apk
git-2.41.0-r0.apk
glibc-2.37-r6.apk
glibc-dev-2.37-r7.apk
glibc-locale-posix-2.37-r7.apk
gmp-6.2.1-r7.apk
go-1.19-1.19.10-r0.apk
isl-0.26-r0.apk
ld-linux-2.37-r7.apk
libatomic-13.1.0-r1.apk
libbrotlicommon1-1.0.9-r3.apk
libbrotlidec1-1.0.9-r3.apk
libcrypto3-3.1.1-r1.apk
libcurl-openssl4-8.1.2-r0.apk
libcurl-openssl4-8.1.2-r1.apk
libedit-3.1-r2.apk
libgcc-13.1.0-r1.apk
libgomp-13.1.0-r1.apk
libnghttp2-14-1.54.0-r0.apk
libpcre2-8-0-10.42-r2.apk
libssl3-3.1.1-r1.apk
libstdc++-13.1.0-r1.apk
libstdc++-dev-13.1.0-r1.apk
linux-headers-5.19.17-r2.apk
make-4.3-r3.apk
mpc-1.3.1-r0.apk
mpfr-4.2.0-r3.apk
ncurses-6.4-r2.apk
ncurses-terminfo-base-6.4-r2.apk
nss-db-2.37-r7.apk
nss-hesiod-2.37-r7.apk
openssh-9.3_p1-r1.apk
openssh-client-9.3_p1-r1.apk
openssh-keygen-9.3_p1-r1.apk
openssh-server-9.3_p1-r1.apk
openssh-sftp-server-9.3_p1-r1.apk
openssl-config-3.1.1-r1.apk
pkgconf-1.9.5-r0.apk
wolfi-baselayout-20230201-r3.apk
zlib-1.2.13-r3.apk

The .etag files are the APKINDEX keyed by their etag value.

I think my initial plan above re: directory structure still makes sense, so I'm going to go with that.

@jonjohnsonjr
Copy link
Contributor Author

Alright, got this working pretty well, I think.

The *.sig.tar.gz files (if they exist) will be named after the hash of the control section that they ostensibly authenticate. I don't believe we actually validate these signatures anywhere, but we probably should. We may want to include the key name in the filename or something. Problems for future me.

This is after the same run I've been doing building go:

.
├── APKINDEX
│   └── f6277a6fc6f79599df2dea203f0995bd.tar.gz
├── bash-5.2.15-r2
│   ├── 43b8153a38bcbbba25caf5a0aee03e439ec9f4c7.ctl.tar.gz
│   ├── 43b8153a38bcbbba25caf5a0aee03e439ec9f4c7.sig.tar.gz
│   └── af60d05ff0ef58717ce822cb2fffc7d3ceafb79e61ab64437b576dd3c5d6fd6d.dat.tar.gz
├── binutils-2.40-r3
│   ├── af2f6616e45c7991885ef6df58e4a9425533b83d.ctl.tar.gz
│   ├── af2f6616e45c7991885ef6df58e4a9425533b83d.sig.tar.gz
│   └── fa83de326e1709f62aa1977e88d76a9504934ab4dc91dd1bf1886e43ef5d948f.dat.tar.gz
├── binutils-gold-2.40-r3
│   ├── 0c60286cf55f7b5e11d16e23c16825d9f923ecbe325ecc8024a39e829d50570c.dat.tar.gz
│   ├── ab9b2798fb7f27c911d208c5eeb9ac98326a5ce1.ctl.tar.gz
│   └── ab9b2798fb7f27c911d208c5eeb9ac98326a5ce1.sig.tar.gz
├── build-base-1-r5
│   ├── 610f8c4c9c684d9d7d916d2887710526f7711eeeb08e9f06ea98810ccfc1cc15.dat.tar.gz
│   ├── 8150c3309f443074c6870a2fdcbfd2cc05ea177e.ctl.tar.gz
│   └── 8150c3309f443074c6870a2fdcbfd2cc05ea177e.sig.tar.gz
├── busybox-1.36.1-r2
│   ├── 7769187bd0f7ffb109f9bf42e9890aaa54ac53f3.ctl.tar.gz
│   ├── 7769187bd0f7ffb109f9bf42e9890aaa54ac53f3.sig.tar.gz
│   └── fd9541ca7a6c4fd8811de9242c07f9f5cb1584c070a0aca32e78c5e77bb67081.dat.tar.gz
├── ca-certificates-bundle-20230506-r0
│   ├── ee7781565f35f736f51e244d696f197ba13548f4ebbdfc72b50c827514db52b8.dat.tar.gz
│   ├── fd81a8de2512772176feb937b4a47a321b541338.ctl.tar.gz
│   └── fd81a8de2512772176feb937b4a47a321b541338.sig.tar.gz
├── expat-2.5.0-r3
│   ├── 7f88320ce2422e26cd26e9b93ada98634d58d031d4aeabe780c2a96d48892d66.dat.tar.gz
│   ├── ae515cdc5e6f06ee6d08334b3bc26a8370ac4466.ctl.tar.gz
│   └── ae515cdc5e6f06ee6d08334b3bc26a8370ac4466.sig.tar.gz
├── gcc-13.1.0-r1
│   ├── 4eb0b1cc794a463e26888f70f7fcbc640b2367a5.ctl.tar.gz
│   ├── 4eb0b1cc794a463e26888f70f7fcbc640b2367a5.sig.tar.gz
│   └── 5f9e1cb40a3ba961ca732e0da04e86c8cb70d9b97ae0c206e380f0c1247c3b01.dat.tar.gz
├── git-2.41.0-r0
│   ├── 5447bda34be2909a9a0a7a038892d3ae1d4db8fd9f543274e220b3e53565f5bc.dat.tar.gz
│   ├── 730dc59d240dae1cd16d9b9dfbe2785951e7df3c.ctl.tar.gz
│   └── 730dc59d240dae1cd16d9b9dfbe2785951e7df3c.sig.tar.gz
├── glibc-2.37-r6
│   ├── 5936599fc0c31226731a28389d67d3807601f511.ctl.tar.gz
│   ├── 5936599fc0c31226731a28389d67d3807601f511.sig.tar.gz
│   └── 68c65729b34c8d1831421608d9e6aa6e55933d7236289853d9b6325b7d175285.dat.tar.gz
├── glibc-dev-2.37-r7
│   ├── 0c7e591e1b443b0199a8d525c2265c3268aa99c8.ctl.tar.gz
│   ├── 0c7e591e1b443b0199a8d525c2265c3268aa99c8.sig.tar.gz
│   └── 62bc33d37d9a55070c50bec24ea91e281adfb05d0823826bd4ca159276ccabab.dat.tar.gz
├── glibc-locale-posix-2.37-r7
│   ├── 238a5de4fa7df658429ce5c586dc9f5f413547fcca2fe3d9799a66b10da62354.dat.tar.gz
│   ├── 3acb9c37e95ec69879830458e60dcf83f1e10e30.ctl.tar.gz
│   └── 3acb9c37e95ec69879830458e60dcf83f1e10e30.sig.tar.gz
├── gmp-6.2.1-r7
│   ├── 28def0ee5142d7e5a3fb0f63defaa25d36a1996e.ctl.tar.gz
│   ├── 28def0ee5142d7e5a3fb0f63defaa25d36a1996e.sig.tar.gz
│   └── d0b9c34f58f4d9df463f0a9770eb535a5b760a427b62fa4be1b47bfe186565dd.dat.tar.gz
├── go-1.19-1.19.10-r0
│   ├── 1a0dac1158da44bd33167cc05fdd1ae2b975f53beff45a45a3e302c868590103.dat.tar.gz
│   ├── b7642e90c2a65a63fd78681f080247fb22dee83e.ctl.tar.gz
│   └── b7642e90c2a65a63fd78681f080247fb22dee83e.sig.tar.gz
├── isl-0.26-r0
│   ├── 40d0e397f4faef3f1b926e5130ce6dab450406f3.ctl.tar.gz
│   ├── 40d0e397f4faef3f1b926e5130ce6dab450406f3.sig.tar.gz
│   └── 5ff4daa6227181df1cba6154651230f246ded248e96cc521b2de4f208b303ac4.dat.tar.gz
├── ld-linux-2.37-r7
│   ├── 981b2311a068ade1bf2ed289b18b6dd48a217aef157ecee696b0961fba0b1d0b.dat.tar.gz
│   ├── bb4b841383e005bbaa4b8946dc9f82f516460e27.ctl.tar.gz
│   └── bb4b841383e005bbaa4b8946dc9f82f516460e27.sig.tar.gz
├── libatomic-13.1.0-r1
│   ├── 28654f5c29bdefa527cc9012fd7890586e98ac92ddb68f48d0bf43c4d720dc0d.dat.tar.gz
│   ├── 8e1efecaed7dff5a7d9e271fd456f302bcf556c0.ctl.tar.gz
│   └── 8e1efecaed7dff5a7d9e271fd456f302bcf556c0.sig.tar.gz
├── libbrotlicommon1-1.0.9-r3
│   ├── 36a0b0874399951ecb6f50f4f80219b24f4fd9549a073fe92569c6e61105a9ec.dat.tar.gz
│   ├── 53fa93e581eaca29cc35babfa6dcab9f436f56a3.ctl.tar.gz
│   └── 53fa93e581eaca29cc35babfa6dcab9f436f56a3.sig.tar.gz
├── libbrotlidec1-1.0.9-r3
│   ├── 4d191ced453a0b91cb11dbb267a29f8b64d98d7ddd8924228a1525210e691624.dat.tar.gz
│   ├── f549ef0d4a74b6d21e83be6b71d29fc5bce4a606.ctl.tar.gz
│   └── f549ef0d4a74b6d21e83be6b71d29fc5bce4a606.sig.tar.gz
├── libcrypto3-3.1.1-r1
│   ├── 25ca4e801e34798ad28de2761f1b5cd9d67f9d28.ctl.tar.gz
│   ├── 25ca4e801e34798ad28de2761f1b5cd9d67f9d28.sig.tar.gz
│   └── a61b9a6ac2d501ab49da602d90a3d94cd4f09dd5266c46758b0b39746cbc5f83.dat.tar.gz
├── libcurl-openssl4-8.1.2-r1
│   ├── 341711f0c231acb735948a273433536b306ee04f.ctl.tar.gz
│   ├── 341711f0c231acb735948a273433536b306ee04f.sig.tar.gz
│   └── 43ac54a1ffbd315df35570910b21abe0175e90b75cd1466acd5e7bd292cc8e4e.dat.tar.gz
├── libedit-3.1-r2
│   ├── 5c2ef9f557e02de7ce56d6e16706738e87e31d565590f4f871a2fcca7122b5db.dat.tar.gz
│   ├── cbcbde66c1fce475fda39135f83ecaf8f790fb0e.ctl.tar.gz
│   └── cbcbde66c1fce475fda39135f83ecaf8f790fb0e.sig.tar.gz
├── libgcc-13.1.0-r1
│   ├── 7b84d6d2502ae3c403083cf819598164fb7110a7757d0b00e9476d326405f25d.dat.tar.gz
│   ├── d92744b402cfe1df77460b50256c35d94b349f06.ctl.tar.gz
│   └── d92744b402cfe1df77460b50256c35d94b349f06.sig.tar.gz
├── libgomp-13.1.0-r1
│   ├── 28fe895d535120c01cd19bb7a0aa74295f5e7cf430c713d83400ea61796e4d8d.dat.tar.gz
│   ├── 55efee81677eaf8f43b284a82ff678eae63c1c15.ctl.tar.gz
│   └── 55efee81677eaf8f43b284a82ff678eae63c1c15.sig.tar.gz
├── libnghttp2-14-1.54.0-r0
│   ├── 04da52e0809bfb875d410eb1066bcd961dc0edcdfd74013b7202f145d5dc4df0.dat.tar.gz
│   ├── 4a1c5ae09142ad4d192f1c2050c517f85e306f31.ctl.tar.gz
│   └── 4a1c5ae09142ad4d192f1c2050c517f85e306f31.sig.tar.gz
├── libpcre2-8-0-10.42-r2
│   ├── 589122e0cdfed588ddd14274b1d6834e0e2fa77c.ctl.tar.gz
│   ├── 589122e0cdfed588ddd14274b1d6834e0e2fa77c.sig.tar.gz
│   └── c6893b990e27abf183e45e4ac8142d10b6aa219f3ff9de68b689789723911a63.dat.tar.gz
├── libssl3-3.1.1-r1
│   ├── 00d88d62707f467531d7531a398ca253270a63fc.ctl.tar.gz
│   ├── 00d88d62707f467531d7531a398ca253270a63fc.sig.tar.gz
│   └── 154811fe8c656a461a6182987fb2bedcc6d1d8a5b536807bb70ba3001c7c25c6.dat.tar.gz
├── libstdc++-13.1.0-r1
│   ├── 11359426d4d88019a0685a8e0e54e43502d43cd84a89eab53aa26f2eb5c806c7.dat.tar.gz
│   ├── c57e932d27a74a01a8348d492412102e55cefa0b.ctl.tar.gz
│   └── c57e932d27a74a01a8348d492412102e55cefa0b.sig.tar.gz
├── libstdc++-dev-13.1.0-r1
│   ├── 24dd2bb9314a4ee4a122546d9cbbb56df37a9bc11ef4af5aa675238eb69b7709.dat.tar.gz
│   ├── f34109916a49fe9eebda14de606539ff73373e68.ctl.tar.gz
│   └── f34109916a49fe9eebda14de606539ff73373e68.sig.tar.gz
├── linux-headers-5.19.17-r2
│   ├── 8dc2522075d4eadf0c3308e5029ac64126e1bb0add07d48ecaba7321c9ee9541.dat.tar.gz
│   ├── cce876a98657acf14a53aba128548b4de86f2247.ctl.tar.gz
│   └── cce876a98657acf14a53aba128548b4de86f2247.sig.tar.gz
├── make-4.3-r3
│   ├── 8b4da17eb6108949a088b7259da4f3a46b53fe1d11cb18c6a8bca6e1f87b28cb.dat.tar.gz
│   ├── e8e490d85af52ee32ed13d145d453efab3439a4a.ctl.tar.gz
│   └── e8e490d85af52ee32ed13d145d453efab3439a4a.sig.tar.gz
├── mpc-1.3.1-r0
│   ├── ac0a9fa8ee68dc2339ff02906aa2776c37be2479.ctl.tar.gz
│   ├── ac0a9fa8ee68dc2339ff02906aa2776c37be2479.sig.tar.gz
│   └── d6619468ca0ff89e8c2bb4a9a3da64a550d37a46caf9d935b92dbbf332357f70.dat.tar.gz
├── mpfr-4.2.0-r3
│   ├── 7ec01857c651fd0409f469bd3d85e6c34b2e0dc4b0b5aa4b238bf6ad4e2378ec.dat.tar.gz
│   ├── 865524987e530d24cf56c6421cea7d7ceb0f03d8.ctl.tar.gz
│   └── 865524987e530d24cf56c6421cea7d7ceb0f03d8.sig.tar.gz
├── ncurses-6.4-r2
│   ├── 60c1d8b5a0b78af4dac53a67a991f5cea20e8463.ctl.tar.gz
│   ├── 60c1d8b5a0b78af4dac53a67a991f5cea20e8463.sig.tar.gz
│   └── 737a1ec4e26bcdf5eb12e427e705e03b4240730af9fd1ef0e90f72f3f2bde411.dat.tar.gz
├── ncurses-terminfo-base-6.4-r2
│   ├── 1b54dec11193959406d08de0c257b918ff0bb7e2d01289095e9f6c09fd2de883.dat.tar.gz
│   ├── 3bd5b215fdaf7b3db61c41c70346a9d9ff966c4b.ctl.tar.gz
│   └── 3bd5b215fdaf7b3db61c41c70346a9d9ff966c4b.sig.tar.gz
├── nss-db-2.37-r7
│   ├── 016f3f40dd5f0db434305cdb84d2cdc3205f3a97.ctl.tar.gz
│   ├── 016f3f40dd5f0db434305cdb84d2cdc3205f3a97.sig.tar.gz
│   └── 91e1d8a5f4b94cee35f4c061998f1f810adb320a1cb9b8f4cef3242bb486dfac.dat.tar.gz
├── nss-hesiod-2.37-r7
│   ├── 11987fc3bf3b74d8ec985594fbd282b2e03a4000.ctl.tar.gz
│   ├── 11987fc3bf3b74d8ec985594fbd282b2e03a4000.sig.tar.gz
│   └── cee5c0b065f522fe0dad59e757e2f46a20d8d928265a05c64cc76c70f7e6a886.dat.tar.gz
├── openssh-9.3_p1-r1
│   ├── 0d4c80d19826df3c153b9277a2479fb55ed2e332.ctl.tar.gz
│   ├── 0d4c80d19826df3c153b9277a2479fb55ed2e332.sig.tar.gz
│   └── 787de69504a2c6b35d3345399a4f30b8d5d10119f10649c14d5da51849b60d41.dat.tar.gz
├── openssh-client-9.3_p1-r1
│   ├── 6802fd15b917edf568da98816f888bde4af172fd.ctl.tar.gz
│   ├── 6802fd15b917edf568da98816f888bde4af172fd.sig.tar.gz
│   └── 92c2ecc188b01f479305915febcf9f84b3072729b3ba0848a2a5ae083a0350e9.dat.tar.gz
├── openssh-keygen-9.3_p1-r1
│   ├── a3032453e7a8b4218248aec496f731273186c005e16c74f72fb104caa19cd8b8.dat.tar.gz
│   ├── eef01bd4e00728d2175806f5c29fa131d3b96cb9.ctl.tar.gz
│   └── eef01bd4e00728d2175806f5c29fa131d3b96cb9.sig.tar.gz
├── openssh-server-9.3_p1-r1
│   ├── 4df5feb3a96faa8fcea59e49006cf6c4100eeff4b80f0aa533480f74996e7f1e.dat.tar.gz
│   ├── f6c15af4161f5afe207cf4af616e192153cfa542.ctl.tar.gz
│   └── f6c15af4161f5afe207cf4af616e192153cfa542.sig.tar.gz
├── openssh-sftp-server-9.3_p1-r1
│   ├── 71136cf6ac1e98aa482d3c12bdee9130f7fccc517f856d1f6a8af3037bfc214e.dat.tar.gz
│   ├── f6556f60a6424a6f62b94edb8a75c0edb443642b.ctl.tar.gz
│   └── f6556f60a6424a6f62b94edb8a75c0edb443642b.sig.tar.gz
├── openssl-config-3.1.1-r1
│   ├── 9b22de2ad6d275f133423dabc7aacfd64105538a23122e297222ac41bab8a223.dat.tar.gz
│   ├── da191d4ce1a18af07e85ef91c01f2b91b06a96bb.ctl.tar.gz
│   └── da191d4ce1a18af07e85ef91c01f2b91b06a96bb.sig.tar.gz
├── pkgconf-1.9.5-r0
│   ├── 45667e4a1ba496cd19beaa5490c540c03552ba55abea3e1a90bd3afa31b22425.dat.tar.gz
│   ├── ce160471ae41898741e7bd70c622fa6c465dde15.ctl.tar.gz
│   └── ce160471ae41898741e7bd70c622fa6c465dde15.sig.tar.gz
├── wolfi-baselayout-20230201-r3
│   ├── 6f7daad885cef029377f0e9b46c76144f9f8152cbc811710c5db17aed751955a.dat.tar.gz
│   ├── d51bae382bc9c8f95034a9dbbb8b6881424cbc19.ctl.tar.gz
│   └── d51bae382bc9c8f95034a9dbbb8b6881424cbc19.sig.tar.gz
└── zlib-1.2.13-r3
    ├── 0591fe39a0957bdd55fdcc18af31005e4680a1da.ctl.tar.gz
    ├── 0591fe39a0957bdd55fdcc18af31005e4680a1da.sig.tar.gz
    └── e85415c797fb3455e1b96e5bef84b8cfe9cf695970713a13a28218122982e1ec.dat.tar.gz

48 directories, 139 files

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant