diff --git a/azure/pom.xml b/azure/pom.xml
index afdb293cfaa..a577a50e4ae 100644
--- a/azure/pom.xml
+++ b/azure/pom.xml
@@ -18,7 +18,7 @@
     
         ch.cyberduck
         parent
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     azure
     
diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java
index cd7876e88ee..9e8af2507f4 100644
--- a/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java
@@ -21,7 +21,7 @@
 import ch.cyberduck.core.DisabledProgressListener;
 import ch.cyberduck.core.Host;
 import ch.cyberduck.core.LoginConnectionService;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.VaultTest;
 
 import org.junit.After;
@@ -34,11 +34,11 @@ public class AbstractAzureTest extends VaultTest {
 
     @Parameterized.Parameters(name = "vaultVersion = {0}")
     public static Object[] data() {
-        return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+        return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
     }
 
     @Parameterized.Parameter
-    public int vaultVersion;
+    public VaultMetadata.Type vaultVersion;
 
     @After
     public void disconnect() throws Exception {
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java
index 72f478660b3..eac1c2205c0 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java
@@ -31,8 +31,10 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -42,7 +44,6 @@
 import java.util.EnumSet;
 
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
@@ -51,9 +52,9 @@ public class AzureDirectoryFeatureTest extends AbstractAzureTest {
     @Test
     public void testMakeDirectoryEncrypted() throws Exception {
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
                 cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
@@ -62,13 +63,15 @@ public void testMakeDirectoryEncrypted() throws Exception {
         cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
     }
 
+
+    //TODO enable
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testMakeDirectoryLongFilenameEncrypted() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
                 cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java
index 174e34b2600..994d385f037 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java
@@ -27,11 +27,13 @@
 import ch.cyberduck.core.cryptomator.features.CryptoListService;
 import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature;
 import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
+import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
 import ch.cyberduck.core.features.Delete;
 import ch.cyberduck.core.shared.DefaultTouchFeature;
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.junit.Test;
@@ -52,9 +54,9 @@ public class AzureListServiceTest extends AbstractAzureTest {
     @Test
     public void testListCryptomator() throws Exception {
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-            new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         assertTrue(new CryptoListService(session, new AzureObjectListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java
index 3aa606d75fc..35a553bdf02 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java
@@ -38,6 +38,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.junit.Test;
@@ -58,9 +59,9 @@ public class AzureMoveFeatureTest extends AbstractAzureTest {
     @Test
     public void testMove() throws Exception {
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final Path folder = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
                 cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java
index b8a257cd4df..a6952a94854 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java
@@ -32,8 +32,10 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -44,19 +46,20 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
 public class AzureTouchFeatureTest extends AbstractAzureTest {
 
+    //TODO
+
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testTouchLongFilenameEncrypted() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final TransferStatus status = new TransferStatus();
         final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
@@ -68,12 +71,12 @@ public void testTouchLongFilenameEncrypted() throws Exception {
     }
 
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testTouchLongFilenameEncryptedDefaultFeature() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final TransferStatus status = new TransferStatus();
         final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java
index a11d3355b97..11c66b56332 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java
@@ -38,6 +38,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.lang3.RandomUtils;
@@ -66,9 +67,9 @@ public void testWrite() throws Exception {
         final byte[] content = RandomUtils.nextBytes(1048576);
         status.setLength(content.length);
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(
-            new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final CryptoWriteFeature writer = new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator);
         final FileHeader header = cryptomator.getFileHeaderCryptor().create();
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
index 1e3bc02b566..459a0c75a3b 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
@@ -47,12 +47,14 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.core.worker.CopyWorker;
 import ch.cyberduck.core.worker.DeleteWorker;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.RandomUtils;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -76,8 +78,8 @@ public void testCopyFile() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         final Path target = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         final byte[] content = RandomUtils.nextBytes(40500);
@@ -102,8 +104,8 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
         final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         final Path targetFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path target = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
@@ -120,11 +122,14 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
         new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session);
     }
 
+    //TODO
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception {
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final Path source = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
         final Path targetFolder = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
         final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
@@ -147,8 +152,9 @@ public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception
     @Test
     public void testCopyFolder() throws Exception {
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
@@ -185,8 +191,8 @@ public void testCopyFileIntoVault() throws Exception {
         assertTrue(new AzureFindFeature(session).find(cleartextFile));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
@@ -214,8 +220,8 @@ public void testCopyDirectoryIntoVault() throws Exception {
         new AzureTouchFeature(session).touch(new AzureWriteFeature(session), cleartextFile, new TransferStatus());
         assertTrue(new AzureFindFeature(session).find(cleartextFolder));
         assertTrue(new AzureFindFeature(session).find(cleartextFile));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         // move directory into vault
@@ -239,8 +245,8 @@ public void testCopyFileOutsideVault() throws Exception {
         new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), clearFolder, new TransferStatus());
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
@@ -265,8 +271,8 @@ public void testCopyDirectoryOutsideVault() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java
index 4ac361ed8c5..008e34a8fd6 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java
@@ -51,6 +51,7 @@
 import ch.cyberduck.core.transfer.UploadTransfer;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.core.worker.SingleTransferWorker;
 import ch.cyberduck.test.IntegrationTest;
 
@@ -77,8 +78,9 @@ public class CryptoAzureSingleTransferWorkerTest extends AbstractAzureTest {
     @Test
     public void testUpload() throws Exception {
         final Path home = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final Path dir1 = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.placeholder));
         final Local localDirectory1 = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random());
diff --git a/backblaze/pom.xml b/backblaze/pom.xml
index 170eeec3f8b..0103dfa5ddd 100644
--- a/backblaze/pom.xml
+++ b/backblaze/pom.xml
@@ -19,7 +19,7 @@
     
         parent
         ch.cyberduck
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     backblaze
     jar
diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/AbstractB2Test.java b/backblaze/src/test/java/ch/cyberduck/core/b2/AbstractB2Test.java
index 898fdd7c710..fa8c3f533ed 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/b2/AbstractB2Test.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/b2/AbstractB2Test.java
@@ -26,10 +26,10 @@
 import ch.cyberduck.core.LoginOptions;
 import ch.cyberduck.core.Profile;
 import ch.cyberduck.core.ProtocolFactory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
 import ch.cyberduck.core.ssl.DefaultX509KeyManager;
 import ch.cyberduck.core.ssl.DefaultX509TrustManager;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.VaultTest;
 
 import org.junit.After;
@@ -47,11 +47,11 @@ public class AbstractB2Test extends VaultTest {
 
     @Parameterized.Parameters(name = "vaultVersion = {0}")
     public static Object[] data() {
-        return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+        return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
     }
 
     @Parameterized.Parameter
-    public int vaultVersion;
+    public VaultMetadata.Type vaultVersion;
 
     @After
     public void disconnect() throws Exception {
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java
index c3207396c44..5dd9b60c437 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java
@@ -39,8 +39,10 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -52,7 +54,6 @@
 import java.util.stream.Collectors;
 
 import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
@@ -61,9 +62,9 @@ public class B2DirectoryFeatureTest extends AbstractB2Test {
     @Test
     public void testMakeDirectoryEncrypted() throws Exception {
         final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
         final Path test = cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -82,12 +83,12 @@ public void testMakeDirectoryEncrypted() throws Exception {
     }
 
     @Test
+    @Ignore("Filename shortening not yet implemented")
     public void testMakeDirectoryLongFilenameEncrypted() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-            new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
         final Path test = cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java
index 868c5e95afb..197fa82e616 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java
@@ -46,6 +46,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.io.IOUtils;
@@ -76,9 +77,9 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
     public void testWrite() throws Exception {
         // 5L * 1024L * 1024L
         final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(
-            new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
         final CryptoUploadFeature service = new CryptoUploadFeature<>(session,
@@ -111,9 +112,9 @@ public void testWrite() throws Exception {
     public void testUploadWithBulk() throws Exception {
         // 5L * 1024L * 1024L
         final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(
-            new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final TransferStatus writeStatus = new TransferStatus();
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadWriteFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadWriteFeatureTest.java
index 664d372a3f5..1a332aba815 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadWriteFeatureTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadWriteFeatureTest.java
@@ -35,6 +35,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.io.IOUtils;
@@ -61,9 +62,9 @@ public class B2LargeUploadWriteFeatureTest extends AbstractB2Test {
     @Test
     public void testWrite() throws Exception {
         final Path container = new Path("test-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(
-            new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
         final CryptoWriteFeature feature = new CryptoWriteFeature<>(session, new B2LargeUploadWriteFeature(session, fileid), cryptomator);
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java
index 4e67fcf8ad0..9b5c0987f7b 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java
@@ -34,6 +34,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.junit.Test;
@@ -56,9 +57,9 @@ public class B2ListServiceTest extends AbstractB2Test {
     @Test
     public void testListCryptomator() throws Exception {
         final Path home = new Path("test-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
         assertTrue(new CryptoListService(session, new B2ListService(session, fileid), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java
index c2744bcfc23..a8fa2ab5208 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java
@@ -36,8 +36,10 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -50,7 +52,6 @@
 import synapticloop.b2.response.BaseB2Response;
 
 import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
@@ -59,9 +60,9 @@ public class B2TouchFeatureTest extends AbstractB2Test {
     @Test
     public void testTouchEncrypted() throws Exception {
         final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
         final TransferStatus status = new TransferStatus();
@@ -76,12 +77,12 @@ public void testTouchEncrypted() throws Exception {
     }
 
     @Test
+    @Ignore("Filename shortening not implemented yet")
     public void testTouchLongFilenameEncrypted() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
         final TransferStatus status = new TransferStatus();
@@ -96,9 +97,9 @@ public void testTouchLongFilenameEncrypted() throws Exception {
     @Test
     public void testTouchEncryptedDefaultFeature() throws Exception {
         final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
         final TransferStatus status = new TransferStatus();
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2WriteFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2WriteFeatureTest.java
index 7713007577b..b955c1c504f 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2WriteFeatureTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2WriteFeatureTest.java
@@ -38,6 +38,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.lang3.RandomUtils;
@@ -69,9 +70,9 @@ public void testWrite() throws Exception {
         final byte[] content = RandomUtils.nextBytes(length);
         status.setLength(content.length);
         final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-            new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
index d54dede8408..a00d8c836e7 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
@@ -46,12 +46,14 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.core.worker.CopyWorker;
 import ch.cyberduck.core.worker.DeleteWorker;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.RandomUtils;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -66,7 +68,6 @@
 import synapticloop.b2.response.BaseB2Response;
 
 import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
@@ -78,8 +79,8 @@ public void testCopyFile() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         final Path target = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         final byte[] content = RandomUtils.nextBytes(40500);
@@ -105,8 +106,8 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
         final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         final Path targetFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path target = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
@@ -125,15 +126,15 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
     }
 
     @Test
+    @Ignore("Filename shortening not implemented yet")
     public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path source = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
         final Path targetFolder = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
         final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
@@ -157,8 +158,8 @@ public void testCopyFolder() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
@@ -194,8 +195,8 @@ public void testCopyFileIntoVault() throws Exception {
         assertTrue(new B2FindFeature(session, fileid).find(cleartextFile));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -221,8 +222,8 @@ public void testCopyDirectoryIntoVault() throws Exception {
         new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), cleartextFile, new TransferStatus());
         assertTrue(new B2FindFeature(session, fileid).find(cleartextFolder));
         assertTrue(new B2FindFeature(session, fileid).find(cleartextFile));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         // move directory into vault
@@ -247,8 +248,8 @@ public void testCopyFileOutsideVault() throws Exception {
         new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), clearFolder, new TransferStatus());
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -273,8 +274,8 @@ public void testCopyDirectoryOutsideVault() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CryptoB2SingleTransferWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CryptoB2SingleTransferWorkerTest.java
index a061a4203d2..64df31ff4ae 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CryptoB2SingleTransferWorkerTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CryptoB2SingleTransferWorkerTest.java
@@ -50,6 +50,7 @@
 import ch.cyberduck.core.transfer.UploadTransfer;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.core.worker.SingleTransferWorker;
 import ch.cyberduck.test.IntegrationTest;
 
@@ -91,8 +92,8 @@ public void testUpload() throws Exception {
         final OutputStream out2 = localFile2.getOutputStream(false);
         IOUtils.write(content, out2);
         out2.close();
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final Transfer t = new UploadTransfer(new Host(new TestProtocol()), Collections.singletonList(new TransferItem(dir1, localDirectory1)), new NullFilter<>());
         assertTrue(new SingleTransferWorker(session, session, t, new TransferOptions(), new TransferSpeedometer(t), new DisabledTransferPrompt() {
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
index 93b8af69087..78bb4a4f431 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
@@ -40,6 +40,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.core.worker.MoveWorker;
 import ch.cyberduck.test.IntegrationTest;
 
@@ -68,8 +69,8 @@ public void testMoveFileIntoVault() throws Exception {
         assertTrue(new DefaultFindFeature(session).find(clearFile));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -95,8 +96,8 @@ public void testMoveDirectoryIntoVault() throws Exception {
         new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), clearFile, new TransferStatus());
         assertTrue(new DefaultFindFeature(session).find(clearFolder));
         assertTrue(new DefaultFindFeature(session).find(clearFile));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         // move directory into vault
@@ -121,8 +122,8 @@ public void testMoveFileOutsideVault() throws Exception {
         new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), clearFolder, new TransferStatus());
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -148,8 +149,8 @@ public void testMoveDirectoryOutsideVault() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
diff --git a/binding/pom.xml b/binding/pom.xml
index ee6406562ff..f59d90638de 100644
--- a/binding/pom.xml
+++ b/binding/pom.xml
@@ -19,7 +19,7 @@
     
         ch.cyberduck
         parent
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     binding
     jar
diff --git a/bonjour/dll/pom.xml b/bonjour/dll/pom.xml
index 80b55d2576d..5d061e3f8d1 100644
--- a/bonjour/dll/pom.xml
+++ b/bonjour/dll/pom.xml
@@ -5,7 +5,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     Cyberduck.Bonjour
     pom
diff --git a/bonjour/native/pom.xml b/bonjour/native/pom.xml
index 7f28e0b32f9..b89c0bfed8a 100644
--- a/bonjour/native/pom.xml
+++ b/bonjour/native/pom.xml
@@ -5,7 +5,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     Cyberduck.Bonjour.Native
     pom
diff --git a/bonjour/pom.xml b/bonjour/pom.xml
index ec313109d6e..cce161af507 100644
--- a/bonjour/pom.xml
+++ b/bonjour/pom.xml
@@ -18,7 +18,7 @@
     
         parent
         ch.cyberduck
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     4.0.0
 
diff --git a/box/pom.xml b/box/pom.xml
index 2e75ca42111..b794f712662 100644
--- a/box/pom.xml
+++ b/box/pom.xml
@@ -19,7 +19,7 @@
     
         parent
         ch.cyberduck
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     box
 
diff --git a/box/src/test/java/ch/cyberduck/core/box/AbstractBoxTest.java b/box/src/test/java/ch/cyberduck/core/box/AbstractBoxTest.java
index 5ece290f673..d3ba73eb22f 100644
--- a/box/src/test/java/ch/cyberduck/core/box/AbstractBoxTest.java
+++ b/box/src/test/java/ch/cyberduck/core/box/AbstractBoxTest.java
@@ -27,10 +27,10 @@
 import ch.cyberduck.core.Profile;
 import ch.cyberduck.core.ProtocolFactory;
 import ch.cyberduck.core.Scheme;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
 import ch.cyberduck.core.ssl.DefaultX509KeyManager;
 import ch.cyberduck.core.ssl.DefaultX509TrustManager;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.VaultTest;
 
 import org.junit.After;
@@ -48,11 +48,11 @@ public class AbstractBoxTest extends VaultTest {
 
     @Parameterized.Parameters(name = "vaultVersion = {0}")
     public static Object[] data() {
-        return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+        return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
     }
 
     @Parameterized.Parameter
-    public int vaultVersion;
+    public VaultMetadata.Type vaultVersion;
 
     @After
     public void disconnect() throws Exception {
diff --git a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java
index 1c604e5f503..e83b706d8af 100644
--- a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java
+++ b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java
@@ -47,6 +47,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.io.IOUtils;
@@ -77,8 +78,8 @@ public void testUploadVaultWithBulkFeature() throws Exception {
         final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L));
         final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
diff --git a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java
index daa3c8cec1a..eceb543152e 100644
--- a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java
+++ b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java
@@ -38,6 +38,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.io.IOUtils;
@@ -66,8 +67,8 @@ public void testWriteVault() throws Exception {
         final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
         final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final CryptoWriteFeature feature = new CryptoWriteFeature<>(session, new BoxWriteFeature(session, fileid), cryptomator);
         final byte[] content = RandomUtils.nextBytes(6 * 1024 * 1024);
@@ -99,8 +100,8 @@ public void testWriteVaultWithTimeStamp() throws Exception {
         final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
         final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final CryptoWriteFeature feature = new CryptoWriteFeature<>(session, new BoxWriteFeature(session, fileid), cryptomator);
         final byte[] content = RandomUtils.nextBytes(6 * 1024 * 1024);
diff --git a/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java b/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java
index 0f36353221c..4eedbd39489 100644
--- a/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java
+++ b/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java
@@ -39,6 +39,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.io.IOUtils;
@@ -66,8 +67,8 @@ public void testWriteVault() throws Exception {
         final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
         final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final CryptoWriteFeature feature = new CryptoWriteFeature<>(session, new BufferWriteFeature(session), cryptomator);
         final byte[] content = RandomUtils.nextBytes(1024 * 1024);
diff --git a/brick/pom.xml b/brick/pom.xml
index c5ef41a7455..0cdf85349b2 100644
--- a/brick/pom.xml
+++ b/brick/pom.xml
@@ -18,7 +18,7 @@
     
         ch.cyberduck
         parent
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     brick
     jar
diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java
index b1ea3afff2c..394d54f73a6 100644
--- a/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java
+++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java
@@ -62,7 +62,7 @@ public Path copy(final Path file, final Path target, final TransferStatus status
             if(entity.getFileMigrationId() != null) {
                 this.poll(client, entity);
             }
-            return new Path(target).withAttributes(new PathAttributes(file.attributes()).setVault(null));
+            return new Path(target).withAttributes(new PathAttributes(file.attributes()).setVaultMetadata(null));
         }
         catch(ApiException e) {
             throw new BrickExceptionMappingService().map("Cannot copy {0}", e, file);
diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java
index c4d356a8c6c..aa7d48d8183 100644
--- a/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java
+++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java
@@ -60,7 +60,7 @@ public Path move(final Path file, final Path target, final TransferStatus status
             if(entity.getFileMigrationId() != null) {
                 this.poll(client, entity);
             }
-            return new Path(target).withAttributes(new PathAttributes(file.attributes()).setVault(null));
+            return new Path(target).withAttributes(new PathAttributes(file.attributes()).setVaultMetadata(null));
         }
         catch(ApiException e) {
             throw new BrickExceptionMappingService().map("Cannot rename {0}", e, file);
diff --git a/brick/src/test/java/ch/cyberduck/core/brick/AbstractBrickTest.java b/brick/src/test/java/ch/cyberduck/core/brick/AbstractBrickTest.java
index 119ddc004c6..7f5ac86fdfb 100644
--- a/brick/src/test/java/ch/cyberduck/core/brick/AbstractBrickTest.java
+++ b/brick/src/test/java/ch/cyberduck/core/brick/AbstractBrickTest.java
@@ -26,10 +26,10 @@
 import ch.cyberduck.core.LoginOptions;
 import ch.cyberduck.core.Profile;
 import ch.cyberduck.core.ProtocolFactory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
 import ch.cyberduck.core.ssl.DefaultX509KeyManager;
 import ch.cyberduck.core.ssl.DefaultX509TrustManager;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.VaultTest;
 
 import org.junit.After;
@@ -47,11 +47,11 @@ public class AbstractBrickTest extends VaultTest {
 
     @Parameterized.Parameters(name = "vaultVersion = {0}")
     public static Object[] data() {
-        return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+        return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
     }
 
     @Parameterized.Parameter
-    public int vaultVersion;
+    public VaultMetadata.Type vaultVersion;
 
     @After
     public void disconnect() throws Exception {
diff --git a/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java b/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java
index 54f85e1b235..7c63c8e6176 100644
--- a/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java
+++ b/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java
@@ -26,7 +26,8 @@
 import ch.cyberduck.core.brick.BrickListService;
 import ch.cyberduck.core.brick.BrickWriteFeature;
 import ch.cyberduck.core.brick.io.swagger.client.model.FileEntity;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.CryptoVaultProvider;
 import ch.cyberduck.core.cryptomator.features.CryptoListService;
 import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature;
 import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
@@ -35,6 +36,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.junit.Assert;
@@ -58,8 +60,8 @@ public void testListCryptomator() throws Exception {
                 EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         assertTrue(new CryptoListService(session, new BrickListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
         new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
diff --git a/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java
index 6e518e70b37..3d96f051ce2 100644
--- a/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java
+++ b/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java
@@ -33,8 +33,10 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -45,19 +47,20 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
 public class DefaultTouchFeatureTest extends AbstractBrickTest {
 
+    //TODO
+
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testTouchLongFilenameEncrypted() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final TransferStatus status = new TransferStatus();
         final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
@@ -68,12 +71,12 @@ public void testTouchLongFilenameEncrypted() throws Exception {
     }
 
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testTouchLongFilenameEncryptedDefaultFeature() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume));
-        final CryptoVault cryptomator = new CryptoVault(
-                new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
-        final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final TransferStatus status = new TransferStatus();
         final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
diff --git a/cli/dll/pom.xml b/cli/dll/pom.xml
index 045f5dc5e3b..28857440673 100644
--- a/cli/dll/pom.xml
+++ b/cli/dll/pom.xml
@@ -20,7 +20,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     Cyberduck.Cli
     pom
diff --git a/cli/linux/pom.xml b/cli/linux/pom.xml
index 2f8896ea363..df5c6636a3f 100644
--- a/cli/linux/pom.xml
+++ b/cli/linux/pom.xml
@@ -20,7 +20,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     cli-linux
     Cyberduck CLI Linux
diff --git a/cli/osx/pom.xml b/cli/osx/pom.xml
index 8c4769b818f..8916ba5d7f0 100644
--- a/cli/osx/pom.xml
+++ b/cli/osx/pom.xml
@@ -20,7 +20,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     cli-osx
     Cyberduck CLI Mac
diff --git a/cli/pom.xml b/cli/pom.xml
index 8a75fa3aa1b..a0b507d645e 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -19,7 +19,7 @@
     
         ch.cyberduck
         parent
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     cli
 
diff --git a/cli/src/main/java/ch/cyberduck/cli/Terminal.java b/cli/src/main/java/ch/cyberduck/cli/Terminal.java
index c7f002840c8..486be082873 100644
--- a/cli/src/main/java/ch/cyberduck/cli/Terminal.java
+++ b/cli/src/main/java/ch/cyberduck/cli/Terminal.java
@@ -48,6 +48,7 @@
 import ch.cyberduck.core.transfer.TransferPrompt;
 import ch.cyberduck.core.transfer.TransferSpeedometer;
 import ch.cyberduck.core.vault.LoadingVaultLookupListener;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.core.vault.VaultRegistry;
 import ch.cyberduck.core.vault.VaultRegistryFactory;
 import ch.cyberduck.core.worker.AttributesWorker;
@@ -247,7 +248,7 @@ public void uncaughtException(final Thread t, final Throwable e) {
                 log.debug("Attempting to load vault from {}", vault);
                 try {
                     this.execute(new TerminalBackgroundAction<>(controller, source, new LoadVaultWorker(new LoadingVaultLookupListener(source.getVaultRegistry(),
-                            new TerminalPasswordCallback()), vault)));
+                            new TerminalPasswordCallback()), new VaultMetadata(vault, VaultMetadata.Type.valueOf(preferences.getProperty("cryptomator.vault.default"))))));
                 }
                 catch(TerminalBackgroundException e) {
                     return Exit.failure;
diff --git a/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java b/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java
index dd13d2bfc4f..9e33bd55c60 100644
--- a/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java
+++ b/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java
@@ -17,7 +17,8 @@
 import ch.cyberduck.core.DisabledConnectionTimeout;
 import ch.cyberduck.core.Local;
 import ch.cyberduck.core.Permission;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.CryptoVaultProvider;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
 import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
 import ch.cyberduck.core.preferences.Preferences;
 import ch.cyberduck.core.transfer.Transfer;
@@ -58,7 +59,9 @@ protected void setFactories() {
         for(Transfer.Type t : Transfer.Type.values()) {
             this.setDefault(String.format("factory.transferpromptcallback.%s.class", t.name()), TerminalTransferPrompt.class.getName());
         }
+        //TODO braucht es diesen eintrag noch?
         this.setDefault("factory.vault.class", CryptoVault.class.getName());
+        this.setDefault("factory.vaultprovider.class", CryptoVaultProvider.class.getName());
         this.setDefault("factory.securerandom.class", FastSecureRandomProvider.class.getName());
         this.setDefault("factory.connectiontimeout.class", DisabledConnectionTimeout.class.getName());
     }
diff --git a/cli/windows/pom.xml b/cli/windows/pom.xml
index b7ef39c6e37..5c9049cec31 100644
--- a/cli/windows/pom.xml
+++ b/cli/windows/pom.xml
@@ -20,7 +20,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     cli-windows
     Cyberduck CLI Windows
diff --git a/core/dll/pom.xml b/core/dll/pom.xml
index 250ef599fbd..484e070b332 100644
--- a/core/dll/pom.xml
+++ b/core/dll/pom.xml
@@ -5,7 +5,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     Cyberduck.Core
     pom
diff --git a/core/dylib/pom.xml b/core/dylib/pom.xml
index dfdbb04f2da..103444e49a7 100644
--- a/core/dylib/pom.xml
+++ b/core/dylib/pom.xml
@@ -5,7 +5,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     libcore
 
diff --git a/core/native/pom.xml b/core/native/pom.xml
index 0a632569837..3c895e72946 100644
--- a/core/native/pom.xml
+++ b/core/native/pom.xml
@@ -5,7 +5,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     Cyberduck.Core.Native
     pom
diff --git a/core/native/refresh/pom.xml b/core/native/refresh/pom.xml
index 3835095fd9e..8e97f0d1e9b 100644
--- a/core/native/refresh/pom.xml
+++ b/core/native/refresh/pom.xml
@@ -5,7 +5,7 @@
         ch.cyberduck
         Cyberduck.Core.Native
         ../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     Cyberduck.Core.Refresh
     pom
diff --git a/core/pom.xml b/core/pom.xml
index f4734797079..5f0027fda6f 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -18,7 +18,7 @@
     
         ch.cyberduck
         parent
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     core
     jar
diff --git a/core/src/main/java/ch/cyberduck/core/PathAttributes.java b/core/src/main/java/ch/cyberduck/core/PathAttributes.java
index 69ae359ef2b..d54f39a3d80 100644
--- a/core/src/main/java/ch/cyberduck/core/PathAttributes.java
+++ b/core/src/main/java/ch/cyberduck/core/PathAttributes.java
@@ -23,6 +23,7 @@
 import ch.cyberduck.core.io.Checksum;
 import ch.cyberduck.core.serializer.Serializer;
 import ch.cyberduck.core.transfer.TransferStatus;
+import ch.cyberduck.core.vault.VaultMetadata;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
@@ -145,6 +146,11 @@ public class PathAttributes extends Attributes implements Serializable {
      * Cryptomator vault
      */
     private Path vault;
+
+    /**
+     * Cryptomator vault metadata
+     */
+    private VaultMetadata vaultMetadata;
     /**
      * Cryptomator decrypted path
      */
@@ -156,7 +162,7 @@ public class PathAttributes extends Attributes implements Serializable {
     /**
      * Unique identifier for cryptomator
      */
-    private String directoryId;
+    private byte[] directoryId;
 
     private Map custom = Collections.emptyMap();
 
@@ -199,6 +205,7 @@ public PathAttributes(final PathAttributes copy) {
         custom = new HashMap<>(copy.custom);
         verdict = copy.verdict;
         vault = copy.vault;
+        vaultMetadata = copy.vaultMetadata;
         decrypted = copy.decrypted;
         encrypted = copy.encrypted;
         directoryId = copy.directoryId;
@@ -287,6 +294,16 @@ public  T serialize(final Serializer dict) {
                 dict.setObjectForKey(vault, "Vault");
             }
         }
+        if(vaultMetadata != null) {
+            if(vaultMetadata.root != null) {
+                if(vaultMetadata.root.attributes() == this) {
+                    log.debug("Skip serializing vault metadata root attribute {} to avoid recursion", vaultMetadata.root);
+                }
+                else {
+                    dict.setObjectForKey(vaultMetadata.root, "Vault Metadata");
+                }
+            }
+        }
         if(!custom.isEmpty()) {
             dict.setMapForKey(custom, "Custom");
         }
@@ -480,11 +497,11 @@ public PathAttributes setLockId(final String lockId) {
         return this;
     }
 
-    public String getDirectoryId() {
+    public byte[] getDirectoryId() {
         return directoryId;
     }
 
-    public PathAttributes setDirectoryId(final String directoryId) {
+    public PathAttributes setDirectoryId(final byte[] directoryId) {
         this.directoryId = directoryId;
         return this;
     }
@@ -534,6 +551,15 @@ public Path getVault() {
         return vault;
     }
 
+    public PathAttributes setVaultMetadata(final VaultMetadata vaultMetadata) {
+        this.vaultMetadata = vaultMetadata;
+        return this;
+    }
+
+    public VaultMetadata getVaultMetadata() {
+        return vaultMetadata;
+    }
+
     /**
      * If the path should not be displayed in a browser by default unless the user explicitly chooses to show hidden
      * files.
@@ -803,7 +829,7 @@ public PathAttributes setLockId(final String lockId) {
         }
 
         @Override
-        public PathAttributes setDirectoryId(final String directoryId) {
+        public PathAttributes setDirectoryId(final byte[] directoryId) {
             return this;
         }
 
diff --git a/core/src/main/java/ch/cyberduck/core/features/Vault.java b/core/src/main/java/ch/cyberduck/core/features/Vault.java
index 23243500404..1267d61d4ca 100644
--- a/core/src/main/java/ch/cyberduck/core/features/Vault.java
+++ b/core/src/main/java/ch/cyberduck/core/features/Vault.java
@@ -24,6 +24,7 @@
 import ch.cyberduck.core.exception.UnsupportedException;
 import ch.cyberduck.core.vault.DisabledVault;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 
 public interface Vault {
 
@@ -35,7 +36,7 @@ public interface Vault {
      * @throws BackgroundException    Failure reading master key from server
      * @throws NotfoundException      No master key file in home
      */
-    Path create(Session> session, String region, VaultCredentials credentials) throws BackgroundException;
+    Vault create(Session> session, String region, VaultCredentials credentials) throws BackgroundException;
 
     /**
      * Open existing vault
@@ -102,6 +103,8 @@ public interface Vault {
      */
     Path getHome();
 
+    VaultMetadata getMetadata();
+
     enum State {
         open,
         closed
diff --git a/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java b/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java
index 0ad7f805a2b..a54bfed364f 100644
--- a/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java
+++ b/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java
@@ -24,6 +24,7 @@
 import ch.cyberduck.core.features.Quota;
 import ch.cyberduck.core.io.Checksum;
 import ch.cyberduck.core.io.HashAlgorithm;
+import ch.cyberduck.core.vault.VaultMetadataDictionary;
 
 import java.util.Collections;
 import java.util.Map;
@@ -117,6 +118,10 @@ public PathAttributes deserialize(final T serialized) {
         if(vaultObj != null) {
             attributes.setVault(new PathDictionary<>(factory).deserialize(vaultObj));
         }
+        final T vaultMetadataObj = dict.objectForKey("Vault Metadata");
+        if(vaultMetadataObj != null) {
+            attributes.setVaultMetadata(new VaultMetadataDictionary<>(factory).deserialize(vaultMetadataObj));
+        }
         final Map customObj = dict.mapForKey("Custom");
         if(customObj != null) {
             attributes.setCustom(customObj);
diff --git a/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java b/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java
index 9550e607bbe..6787da34a05 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java
@@ -23,14 +23,12 @@
 import ch.cyberduck.core.SimplePathPredicate;
 import ch.cyberduck.core.UrlProvider;
 import ch.cyberduck.core.features.*;
-import ch.cyberduck.core.preferences.HostPreferencesFactory;
 import ch.cyberduck.core.preferences.PreferencesFactory;
 import ch.cyberduck.core.vault.registry.*;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.concurrent.CopyOnWriteArraySet;
 
@@ -41,9 +39,14 @@ public class DefaultVaultRegistry extends CopyOnWriteArraySet implements
         PreferencesFactory.get().getProperty("cryptomator.vault.masterkey.filename");
     public static final String DEFAULT_BACKUPKEY_FILE_NAME = String.format("%s.bkup",
         PreferencesFactory.get().getProperty("cryptomator.vault.masterkey.filename"));
+
+    // TODO können die weg? wird z.b. weiter unten via hostpreferences geholt
     public static final String DEFAULT_VAULTCONFIG_FILE_NAME =
         PreferencesFactory.get().getProperty("cryptomator.vault.config.filename");
 
+    public static final String DEFAULT_VAULTCONFIGUVF_FILE_NAME =
+            PreferencesFactory.get().getProperty("cryptomator.vault.config.filename.uvf");
+
     private final PasswordCallback prompt;
 
     public DefaultVaultRegistry(final PasswordCallback prompt) {
@@ -73,7 +76,7 @@ public boolean close(final Path directory) {
             });
         }
         finally {
-            directory.attributes().setVault(null);
+            directory.attributes().setVaultMetadata(null);
         }
     }
 
@@ -107,18 +110,14 @@ public Vault find(final Session> session, final Path file, final boolean autol
         }
         if(autoload) {
             final LoadingVaultLookupListener listener = new LoadingVaultLookupListener(this, prompt);
-            if(file.attributes().getVault() != null) {
-                return listener.load(session, file.attributes().getVault(),
-                        HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"),
-                        HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"),
-                        HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8));
+            if(file.attributes().getVaultMetadata() != null) {
+                return listener.load(session, file.attributes().getVaultMetadata()
+                );
             }
             final Path directory = file.getParent();
-            if(directory.attributes().getVault() != null) {
-                return listener.load(session, directory.attributes().getVault(),
-                        HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"),
-                        HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"),
-                        HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8));
+            if(directory.attributes().getVaultMetadata() != null) {
+                return listener.load(session, directory.attributes().getVaultMetadata()
+                );
             }
         }
         return Vault.DISABLED;
diff --git a/core/src/main/java/ch/cyberduck/core/vault/DisabledVault.java b/core/src/main/java/ch/cyberduck/core/vault/DisabledVault.java
index 70d119aede1..76b7d0541f2 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/DisabledVault.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/DisabledVault.java
@@ -36,7 +36,7 @@ public DisabledVault(final Path home) {
     }
 
     @Override
-    public Path create(final Session> session, final String region, final VaultCredentials credentials) {
+    public Vault create(final Session> session, final String region, final VaultCredentials credentials) {
         return null;
     }
 
@@ -96,6 +96,10 @@ public Path getHome() {
         return home;
     }
 
+    @Override
+    public VaultMetadata getMetadata() {
+        return null;
+    }
 
     @Override
     public boolean equals(final Object o) {
diff --git a/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultLookupListener.java b/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultLookupListener.java
index f1cd16e3f11..2eff02bead2 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultLookupListener.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultLookupListener.java
@@ -15,7 +15,6 @@
  * GNU General Public License for more details.
  */
 
-import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
 import ch.cyberduck.core.features.Vault;
 
@@ -26,8 +25,8 @@ public final class DisabledVaultLookupListener implements VaultLookupListener {
     private static final Logger log = LogManager.getLogger(DisabledVaultLookupListener.class);
 
     @Override
-    public Vault load(final Session session, final Path directory, final String masterkey, final String config, final byte[] pepper) {
-        log.warn("Ignore vault {}", directory);
+    public Vault load(final Session session, final VaultMetadata metadata) {
+        log.warn("Ignore vault {}", metadata);
         return Vault.DISABLED;
     }
 }
diff --git a/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultProvider.java b/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultProvider.java
new file mode 100644
index 00000000000..e096580ac26
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultProvider.java
@@ -0,0 +1,50 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.ListProgressListener;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.features.Find;
+import ch.cyberduck.core.features.Vault;
+
+public class DisabledVaultProvider implements VaultProvider {
+    @Override
+    public boolean isVault(final Path path) {
+        return false;
+    }
+
+    @Override
+    public VaultMetadata metadata(final Path path) {
+        return null;
+    }
+
+    @Override
+    public VaultMetadata find(final Path directory, final Find find, final ListProgressListener listener) throws BackgroundException {
+        return null;
+    }
+
+    @Override
+    public Vault provide(final Session> session, final VaultMetadata metadata) {
+        return Vault.DISABLED;
+    }
+
+    @Override
+    public Vault create(final Session> session, final String region, final VaultCredentials credentials, final VaultMetadata metadata) throws BackgroundException {
+        return Vault.DISABLED;
+    }
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/LoadingVaultLookupListener.java b/core/src/main/java/ch/cyberduck/core/vault/LoadingVaultLookupListener.java
index 242f903b8d5..49ed4fb9d3d 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/LoadingVaultLookupListener.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/LoadingVaultLookupListener.java
@@ -16,8 +16,6 @@
  */
 
 import ch.cyberduck.core.PasswordCallback;
-import ch.cyberduck.core.PasswordStore;
-import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Vault;
@@ -37,21 +35,21 @@ public LoadingVaultLookupListener(final VaultRegistry registry, final PasswordCa
     }
 
     @Override
-    public Vault load(final Session session, final Path directory, final String masterkey, final String config, final byte[] pepper) throws VaultUnlockCancelException {
+    public Vault load(final Session session, final VaultMetadata metadata) throws VaultUnlockCancelException {
         synchronized(registry) {
-            if(registry.contains(directory)) {
-                return registry.find(session, directory);
+            if(registry.contains(metadata.root)) {
+                return registry.find(session, metadata.root);
             }
-            final Vault vault = VaultFactory.get(directory, masterkey, config, pepper);
-            log.info("Loading vault {} for session {}", vault, session);
+            log.info("Loading vault for session {}", session);
+            final Vault vault = VaultProviderFactory.get(session).provide(session, metadata);
             try {
                 registry.add(vault.load(session, prompt));
+                return vault;
             }
             catch(BackgroundException e) {
-                log.warn("Failure {} loading vault {}", e, vault);
+                log.warn("Failure {} loading vault", e);
                 throw new VaultUnlockCancelException(vault, e);
             }
-            return vault;
         }
     }
 }
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultFinderListProgressListener.java b/core/src/main/java/ch/cyberduck/core/vault/VaultFinderListProgressListener.java
index 4f9eb821ce2..4c0249678e9 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/VaultFinderListProgressListener.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultFinderListProgressListener.java
@@ -23,12 +23,10 @@
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.exception.ConnectionCanceledException;
 import ch.cyberduck.core.features.Vault;
-import ch.cyberduck.core.preferences.HostPreferencesFactory;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-import java.nio.charset.StandardCharsets;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -38,21 +36,17 @@ public class VaultFinderListProgressListener extends IndexedListProgressListener
     private final Session> session;
     private final VaultLookupListener lookup;
     private final ListProgressListener proxy;
-    private final String config;
-    private final String masterkey;
-    private final byte[] pepper;
     // Number of files to wait for until proxy is notified of files
     private final int filecount;
     private final AtomicBoolean canceled = new AtomicBoolean();
+    private final VaultProvider provider;
 
     public VaultFinderListProgressListener(final Session> session, final VaultLookupListener lookup, final ListProgressListener proxy, final int filecount) {
         this.session = session;
         this.lookup = lookup;
         this.proxy = proxy;
-        this.config = HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename");
-        this.masterkey = HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename");
-        this.pepper = HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8);
         this.filecount = filecount;
+        this.provider = VaultProviderFactory.get(session);
     }
 
     @Override
@@ -83,10 +77,10 @@ public void chunk(final Path folder, final AttributedList list) throws Con
 
     @Override
     public void visit(final AttributedList list, final int index, final Path file) throws ConnectionCanceledException {
-        final Path directory = file.getParent();
-        if(config.equals(file.getName()) || masterkey.equals(file.getName())) {
+        final VaultMetadata metadata = provider.metadata(file);
+        if(metadata != null) {
             log.info("Found vault config or masterkey file {}", file);
-            final Vault vault = lookup.load(session, directory, masterkey, config, pepper);
+            final Vault vault = lookup.load(session, metadata);
             if(vault.equals(Vault.DISABLED)) {
                 return;
             }
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultLookupListener.java b/core/src/main/java/ch/cyberduck/core/vault/VaultLookupListener.java
index 7e4f12cbbb2..56782de4096 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/VaultLookupListener.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultLookupListener.java
@@ -15,10 +15,9 @@
  * GNU General Public License for more details.
  */
 
-import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
 import ch.cyberduck.core.features.Vault;
 
 public interface VaultLookupListener {
-    Vault load(final Session session, Path directory, String masterkey, final String config, byte[] pepper) throws VaultUnlockCancelException;
+    Vault load(final Session session, VaultMetadata metadata) throws VaultUnlockCancelException;
 }
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultMetadata.java b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadata.java
new file mode 100644
index 00000000000..468b609d344
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadata.java
@@ -0,0 +1,87 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Serializable;
+import ch.cyberduck.core.serializer.Serializer;
+
+import java.util.Objects;
+
+public class VaultMetadata implements Serializable {
+
+    public Path root;
+    public Type type;
+
+    public enum Type {
+        V8, UVF
+    }
+
+    public VaultMetadata() {
+    }
+
+    public VaultMetadata(final Path path, final Type type) {
+        this.root = path;
+        this.type = type;
+    }
+
+    @Override
+    public  T serialize(final Serializer dict) {
+        if(root != null) {
+            dict.setObjectForKey(root, "Root");
+        }
+        if(type != null) {
+            dict.setStringForKey(type.name(), "Type");
+        }
+        return dict.getSerialized();
+    }
+
+    @Override
+    public final boolean equals(final Object o) {
+        if(o == this) {
+            return true;
+        }
+        if(!(o instanceof VaultMetadata)) {
+            return false;
+        }
+
+        VaultMetadata that = (VaultMetadata) o;
+        if(!Objects.equals(root, that.root)) {
+            return false;
+        }
+        if(type != that.type) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hashCode(root);
+        result = 31 * result + Objects.hashCode(type);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("VaultMetadata{");
+        sb.append("root=").append(root);
+        sb.append(", type=").append(type);
+        sb.append('}');
+        return sb.toString();
+    }
+}
+
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultMetadataDictionary.java b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadataDictionary.java
new file mode 100644
index 00000000000..e3bd813a80e
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadataDictionary.java
@@ -0,0 +1,47 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.DeserializerFactory;
+import ch.cyberduck.core.serializer.Deserializer;
+import ch.cyberduck.core.serializer.PathDictionary;
+
+public class VaultMetadataDictionary {
+
+    private final DeserializerFactory factory;
+
+    public VaultMetadataDictionary() {
+        this.factory = new DeserializerFactory<>();
+    }
+
+    public VaultMetadataDictionary(final DeserializerFactory factory) {
+        this.factory = factory;
+    }
+
+    public VaultMetadata deserialize(final T serialized) {
+        final Deserializer dict = factory.create(serialized);
+        final VaultMetadata vaultMetadata = new VaultMetadata();
+        final T vaultObj = dict.objectForKey("Root");
+        if(vaultObj != null) {
+            vaultMetadata.root = new PathDictionary<>(factory).deserialize(vaultObj);
+        }
+        final String type = dict.stringForKey("Type");
+        if(type != null) {
+            vaultMetadata.type = VaultMetadata.Type.valueOf(type);
+        }
+        return vaultMetadata;
+    }
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultProvider.java b/core/src/main/java/ch/cyberduck/core/vault/VaultProvider.java
new file mode 100644
index 00000000000..7415052e739
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultProvider.java
@@ -0,0 +1,37 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.ListProgressListener;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.features.Find;
+import ch.cyberduck.core.features.Vault;
+
+public interface VaultProvider {
+    boolean isVault(Path path);
+
+    VaultMetadata metadata(Path path);
+
+    VaultMetadata find(Path directory, Find find, ListProgressListener listener) throws BackgroundException;
+
+    Vault provide(Session> session, VaultMetadata metadata);
+
+    Vault create(Session> session, String region, VaultCredentials credentials, VaultMetadata metadata) throws BackgroundException;
+
+    VaultProvider DISABLED = new DisabledVaultProvider();
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultProviderFactory.java b/core/src/main/java/ch/cyberduck/core/vault/VaultProviderFactory.java
new file mode 100644
index 00000000000..172fc59d255
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultProviderFactory.java
@@ -0,0 +1,55 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.Factory;
+import ch.cyberduck.core.Session;
+
+import org.apache.commons.lang3.reflect.ConstructorUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+public class VaultProviderFactory extends Factory {
+    private static final Logger log = LogManager.getLogger(VaultProviderFactory.class);
+
+    private VaultProviderFactory() {
+        super("factory.vaultprovider.class");
+    }
+
+    public static VaultProvider get(final Session> session) {
+        return new VaultProviderFactory().create(session);
+    }
+
+    private VaultProvider create(final Session> session) {
+        try {
+            final Constructor extends VaultProvider> constructor = ConstructorUtils.getMatchingAccessibleConstructor(clazz,
+                    session.getClass());
+            if(null == constructor) {
+                log.warn("No matching constructor for parameter {}", session.getClass());
+                // Call default constructor for disabled implementations
+                return clazz.getDeclaredConstructor().newInstance();
+            }
+            return constructor.newInstance(session);
+        }
+        catch(InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
+            log.error("Failure loading callback class {}. {}", clazz, e.getMessage());
+            return VaultProvider.DISABLED;
+        }
+    }
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java
index 90570bc7c35..c05b9d2a0c5 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java
@@ -23,13 +23,15 @@
 import ch.cyberduck.core.features.Vault;
 import ch.cyberduck.core.preferences.HostPreferencesFactory;
 import ch.cyberduck.core.vault.VaultLookupListener;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultProvider;
+import ch.cyberduck.core.vault.VaultProviderFactory;
 import ch.cyberduck.core.vault.VaultRegistry;
 import ch.cyberduck.core.vault.VaultUnlockCancelException;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-import java.nio.charset.StandardCharsets;
 import java.util.EnumSet;
 
 public class VaultRegistryFindFeature implements Find {
@@ -40,6 +42,7 @@ public class VaultRegistryFindFeature implements Find {
     private final VaultRegistry registry;
     private final VaultLookupListener lookup;
     private final boolean autodetect;
+    private final VaultProvider provider;
 
     public VaultRegistryFindFeature(final Session> session, final Find proxy, final VaultRegistry registry, final VaultLookupListener lookup) {
         this.session = session;
@@ -48,6 +51,7 @@ public VaultRegistryFindFeature(final Session> session, final Find proxy, fina
         this.lookup = lookup;
         this.autodetect = HostPreferencesFactory.get(session.getHost()).getBoolean("cryptomator.vault.autodetect")
                 && HostPreferencesFactory.get(session.getHost()).getBoolean("cryptomator.enable");
+        this.provider = VaultProviderFactory.get(session);
     }
 
     @Override
@@ -60,14 +64,13 @@ public boolean find(final Path file, final ListProgressListener listener) throws
                         HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"), EnumSet.of(Path.Type.file));
                 final Path key = new Path(directory,
                         HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"), EnumSet.of(Path.Type.file));
-                if(proxy.find(vaultConfig, listener) || proxy.find(key, listener)) {
+
+                final VaultMetadata metadata = provider.find(directory, proxy, listener);
+                if(metadata != null) {
                     log.info("Found vault config {} or masterkey {}", vaultConfig, key);
                     try {
                         log.info("Found vault {}", directory);
-                        return lookup.load(session, directory,
-                                        HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"),
-                                        HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"),
-                                        HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8))
+                        return lookup.load(session, metadata)
                                 .getFeature(session, Find.class, proxy)
                                 .find(file, listener);
                     }
diff --git a/core/src/main/java/ch/cyberduck/core/worker/CreateVaultWorker.java b/core/src/main/java/ch/cyberduck/core/worker/CreateVaultWorker.java
index 85d274acccf..7dedd68669b 100644
--- a/core/src/main/java/ch/cyberduck/core/worker/CreateVaultWorker.java
+++ b/core/src/main/java/ch/cyberduck/core/worker/CreateVaultWorker.java
@@ -16,38 +16,38 @@
  */
 
 import ch.cyberduck.core.LocaleFactory;
-import ch.cyberduck.core.PasswordStore;
-import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Vault;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultProviderFactory;
 
 import java.text.MessageFormat;
 import java.util.Objects;
 
-public class CreateVaultWorker extends Worker {
+public class CreateVaultWorker extends Worker {
 
     private final String region;
     private final VaultCredentials passphrase;
-    private final Vault vault;
+    private final VaultMetadata metadata;
 
-    public CreateVaultWorker(final String region, final VaultCredentials passphrase, final Vault vault) {
+    public CreateVaultWorker(final String region, final VaultCredentials passphrase, final VaultMetadata metadata) {
         this.region = region;
         this.passphrase = passphrase;
-        this.vault = vault;
+        this.metadata = metadata;
     }
 
     @Override
-    public Path run(final Session> session) throws BackgroundException {
-        final Path home = vault.create(session, region, passphrase);
+    public Vault run(final Session> session) throws BackgroundException {
+        final Vault vault = VaultProviderFactory.get(session).create(session, region, passphrase, metadata);
         vault.close();
-        return home;
+        return vault;
     }
 
     @Override
     public String getActivity() {
-        return MessageFormat.format(LocaleFactory.localizedString("Making directory {0}", "Status"), vault.getHome().getName());
+        return MessageFormat.format(LocaleFactory.localizedString("Making directory {0}", "Status"), metadata.root.getName());
     }
 
     @Override
@@ -59,18 +59,18 @@ public boolean equals(final Object o) {
             return false;
         }
         final CreateVaultWorker that = (CreateVaultWorker) o;
-        return Objects.equals(vault, that.vault);
+        return Objects.equals(metadata, that.metadata);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(vault);
+        return Objects.hash(metadata);
     }
 
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("CreateVaultWorker{");
-        sb.append("vault=").append(vault);
+        sb.append("metadata=").append(metadata);
         sb.append('}');
         return sb.toString();
     }
diff --git a/core/src/main/java/ch/cyberduck/core/worker/LoadVaultWorker.java b/core/src/main/java/ch/cyberduck/core/worker/LoadVaultWorker.java
index 334d3b2c189..1b56778c45e 100644
--- a/core/src/main/java/ch/cyberduck/core/worker/LoadVaultWorker.java
+++ b/core/src/main/java/ch/cyberduck/core/worker/LoadVaultWorker.java
@@ -16,32 +16,27 @@
  */
 
 import ch.cyberduck.core.LocaleFactory;
-import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Vault;
-import ch.cyberduck.core.preferences.HostPreferencesFactory;
 import ch.cyberduck.core.vault.VaultLookupListener;
+import ch.cyberduck.core.vault.VaultMetadata;
 
-import java.nio.charset.StandardCharsets;
 import java.util.Objects;
 
 public class LoadVaultWorker extends Worker {
 
     private final VaultLookupListener listener;
-    private final Path directory;
+    private final VaultMetadata metadata;
 
-    public LoadVaultWorker(final VaultLookupListener listener, final Path directory) {
+    public LoadVaultWorker(final VaultLookupListener listener, final VaultMetadata metadata) {
         this.listener = listener;
-        this.directory = directory;
+        this.metadata = metadata;
     }
 
     @Override
     public Vault run(final Session> session) throws BackgroundException {
-        return listener.load(session, directory,
-                HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"),
-                HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"),
-                HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8));
+        return listener.load(session, metadata);
     }
 
     @Override
@@ -58,18 +53,18 @@ public boolean equals(final Object o) {
             return false;
         }
         final LoadVaultWorker that = (LoadVaultWorker) o;
-        return Objects.equals(directory, that.directory);
+        return Objects.equals(metadata, that.metadata);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(directory);
+        return Objects.hash(metadata);
     }
 
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("LoadVaultWorker{");
-        sb.append("directory=").append(directory);
+        sb.append("metadata=").append(metadata);
         sb.append('}');
         return sb.toString();
     }
diff --git a/cryptomator/dll/pom.xml b/cryptomator/dll/pom.xml
index da67b9b2759..c9e6351c51d 100644
--- a/cryptomator/dll/pom.xml
+++ b/cryptomator/dll/pom.xml
@@ -20,7 +20,7 @@
         ch.cyberduck
         parent
         ../../pom.xml
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     Cyberduck.Cryptomator
     pom
diff --git a/cryptomator/pom.xml b/cryptomator/pom.xml
index 22b797aa85e..da6f89fc77b 100644
--- a/cryptomator/pom.xml
+++ b/cryptomator/pom.xml
@@ -19,13 +19,13 @@
     
         parent
         ch.cyberduck
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     cryptomator
     jar
 
     
-        2.1.2.1
+        2.3.0.uvfdraft-SNAPSHOT
     
 
     
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java
new file mode 100644
index 00000000000..03739370d5c
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java
@@ -0,0 +1,421 @@
+package ch.cyberduck.core.cryptomator;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.ListService;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.PathAttributes;
+import ch.cyberduck.core.Permission;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.UrlProvider;
+import ch.cyberduck.core.cryptomator.features.*;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.features.*;
+import ch.cyberduck.core.shared.DefaultTouchFeature;
+import ch.cyberduck.core.transfer.TransferStatus;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.AuthenticationFailedException;
+import org.cryptomator.cryptolib.api.Cryptor;
+import org.cryptomator.cryptolib.api.DirectoryContentCryptor;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+import org.cryptomator.cryptolib.api.FileContentCryptor;
+import org.cryptomator.cryptolib.api.FileHeaderCryptor;
+import org.cryptomator.cryptolib.api.Masterkey;
+
+import java.util.EnumSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class AbstractVault implements Vault {
+
+    private static final Logger log = LogManager.getLogger(AbstractVault.class);
+
+    public abstract Path getMasterkeyPath();
+
+    public abstract Masterkey getMasterkey();
+
+    public abstract Path getConfig();
+
+    public abstract FileHeaderCryptor getFileHeaderCryptor();
+
+    public abstract FileContentCryptor getFileContentCryptor();
+
+    public abstract CryptoFilename getFilenameProvider();
+
+    public abstract CryptoDirectory getDirectoryProvider();
+
+    public abstract Cryptor getCryptor();
+
+    public abstract int getNonceSize();
+
+    public abstract Pattern getFilenamePattern();
+
+    public int numberOfChunks(long cleartextFileSize) {
+        return (int) (cleartextFileSize / this.getFileContentCryptor().cleartextChunkSize() +
+                ((cleartextFileSize % this.getFileContentCryptor().cleartextChunkSize() > 0) ? 1 : 0));
+    }
+
+    public long toCleartextSize(final long cleartextFileOffset, final long ciphertextFileSize) throws CryptoInvalidFilesizeException {
+        if(TransferStatus.UNKNOWN_LENGTH == ciphertextFileSize) {
+            return TransferStatus.UNKNOWN_LENGTH;
+        }
+        final int headerSize;
+        if(0L == cleartextFileOffset) {
+            headerSize = this.getFileHeaderCryptor().headerSize();
+        }
+        else {
+            headerSize = 0;
+        }
+        try {
+            return this.getFileContentCryptor().cleartextSize(ciphertextFileSize - headerSize);
+        }
+        catch(AssertionError e) {
+            throw new CryptoInvalidFilesizeException(String.format("Encrypted file size must be at least %d bytes", headerSize));
+        }
+        catch(IllegalArgumentException e) {
+            throw new CryptoInvalidFilesizeException(String.format("Invalid file size. %s", e.getMessage()));
+        }
+    }
+
+    @Override
+    public State getState() {
+        return this.isUnlocked() ? State.open : State.closed;
+    }
+
+    @Override
+    public long toCiphertextSize(final long cleartextFileOffset, final long cleartextFileSize) {
+        if(TransferStatus.UNKNOWN_LENGTH == cleartextFileSize) {
+            return TransferStatus.UNKNOWN_LENGTH;
+        }
+        final int headerSize;
+        if(0L == cleartextFileOffset) {
+            headerSize = this.getCryptor().fileHeaderCryptor().headerSize();
+        }
+        else {
+            headerSize = 0;
+        }
+        return headerSize + this.getCryptor().fileContentCryptor().ciphertextSize(cleartextFileSize);
+    }
+
+    @Override
+    public Path encrypt(Session> session, Path file) throws BackgroundException {
+        return this.encrypt(session, file, false);
+    }
+
+    @Override
+    public Path encrypt(Session> session, Path file, boolean metadata) throws BackgroundException {
+        final Path encrypted;
+        if(file.isFile() || metadata) {
+            if(file.getType().contains(Path.Type.vault)) {
+                log.warn("Skip file {} because it is marked as an internal vault path", file);
+                return file;
+            }
+            if(new SimplePathPredicate(file).test(this.getHome())) {
+                log.warn("Skip vault home {} because the root has no metadata file", file);
+                return file;
+            }
+            final Path parent;
+            final String filename;
+            if(file.getType().contains(Path.Type.encrypted)) {
+                final Path decrypted = file.attributes().getDecrypted();
+                parent = this.getDirectoryProvider().toEncrypted(session, decrypted.getParent());
+                filename = this.getDirectoryProvider().toEncrypted(session, decrypted.getParent(), decrypted.getName(), decrypted.getType());
+            }
+            else {
+                parent = this.getDirectoryProvider().toEncrypted(session, file.getParent());
+                filename = this.getDirectoryProvider().toEncrypted(session, file.getParent(), file.getName(), file.getType());
+            }
+            final PathAttributes attributes = new PathAttributes(file.attributes());
+            attributes.setDirectoryId(null);
+            if(!file.isFile() && !metadata) {
+                // The directory is different from the metadata file used to resolve the actual folder
+                attributes.setVersionId(null);
+                attributes.setFileId(null);
+            }
+            // Translate file size
+            attributes.setSize(this.toCiphertextSize(0L, file.attributes().getSize()));
+            final EnumSet type = EnumSet.copyOf(file.getType());
+            type.remove(Path.Type.decrypted);
+            type.add(Path.Type.encrypted);
+            encrypted = new Path(parent, filename, type, attributes);
+        }
+        else {
+            if(file.getType().contains(Path.Type.encrypted)) {
+                log.warn("Skip file {} because it is already marked as an encrypted path", file);
+                return file;
+            }
+            if(file.getType().contains(Path.Type.vault)) {
+                return this.getDirectoryProvider().toEncrypted(session, this.getHome());
+            }
+            encrypted = this.getDirectoryProvider().toEncrypted(session, file);
+        }
+        // Add reference to decrypted file
+        if(!file.getType().contains(Path.Type.encrypted)) {
+            encrypted.attributes().setDecrypted(file);
+        }
+        // Add reference for vault
+        file.attributes().setVaultMetadata(this.getMetadata());
+        encrypted.attributes().setVaultMetadata(this.getMetadata());
+        return encrypted;
+    }
+
+    @Override
+    public Path decrypt(final Session> session, final Path file) throws BackgroundException {
+        if(file.getType().contains(Path.Type.decrypted)) {
+            log.warn("Skip file {} because it is already marked as a decrypted path", file);
+            return file;
+        }
+        if(file.getType().contains(Path.Type.vault)) {
+            log.warn("Skip file {} because it is marked as an internal vault path", file);
+            return file;
+        }
+        final Path inflated = this.inflate(session, file);
+        final Pattern pattern = this.getFilenamePattern();
+        final Matcher m = pattern.matcher(inflated.getName());
+        if(m.matches()) {
+            try {
+                //TODO lädt das recovery metadaten file anstatt normales
+                final DirectoryContentCryptor.Decrypting decrypting = this.getFilenameDecryptor(session, file);
+                //TODO hier hatten wir caching via CryptorCache
+                final String cleartextFilename = decrypting.decrypt(inflated.getName());
+                final PathAttributes attributes = new PathAttributes(file.attributes());
+                if(this.isDirectory(inflated)) {
+                    if(Permission.EMPTY != attributes.getPermission()) {
+                        final Permission permission = new Permission(attributes.getPermission());
+                        permission.setUser(permission.getUser().or(Permission.Action.execute));
+                        permission.setGroup(permission.getGroup().or(Permission.Action.execute));
+                        permission.setOther(permission.getOther().or(Permission.Action.execute));
+                        attributes.setPermission(permission);
+                    }
+                    // Reset size for folders
+                    attributes.setSize(-1L);
+                    attributes.setVersionId(null);
+                    attributes.setFileId(null);
+                }
+                else {
+                    // Translate file size
+                    attributes.setSize(this.toCleartextSize(0L, file.attributes().getSize()));
+                }
+                // Add reference to encrypted file
+                attributes.setEncrypted(file);
+                // Add reference for vault
+                attributes.setVaultMetadata(this.getMetadata());
+                final EnumSet type = EnumSet.copyOf(file.getType());
+                type.remove(this.isDirectory(inflated) ? Path.Type.file : Path.Type.directory);
+                type.add(this.isDirectory(inflated) ? Path.Type.directory : Path.Type.file);
+                type.remove(Path.Type.encrypted);
+                type.add(Path.Type.decrypted);
+                final Path decrypted = new Path(file.getParent().attributes().getDecrypted(), cleartextFilename, type, attributes);
+                if(type.contains(Path.Type.symboliclink)) {
+                    decrypted.setSymlinkTarget(file.getSymlinkTarget());
+                }
+                return decrypted;
+            }
+            catch(AuthenticationFailedException e) {
+                throw new CryptoAuthenticationException(
+                        "Failure to decrypt due to an unauthentic ciphertext", e);
+            }
+        }
+        else {
+            throw new CryptoFilenameMismatchException(
+                    String.format("Failure to decrypt %s due to missing pattern match for %s", inflated.getName(), pattern));
+        }
+    }
+
+    private DirectoryContentCryptor.Decrypting getFilenameDecryptor(final Session> session, final Path directory) throws BackgroundException {
+        // Read directory id from file
+        log.debug("Read directory ID from {}", directory);
+        final DirectoryMetadata metadata = this.getDirectoryProvider().getOrCreateDirectoryId(session, directory.getParent());
+        return this.getCryptor().directoryContentCryptor().fileNameDecryptor(metadata);
+    }
+
+    private boolean isDirectory(final Path p) {
+        return p.isDirectory();
+    }
+
+    private Path inflate(final Session> session, final Path file) throws BackgroundException {
+        final String fileName = file.getName();
+        if(this.getFilenameProvider().isDeflated(fileName)) {
+            final String filename = this.getFilenameProvider().inflate(session, fileName);
+            return new Path(file.getParent(), filename, EnumSet.of(Path.Type.file), file.attributes());
+        }
+        return file;
+    }
+
+    public synchronized boolean isUnlocked() {
+        return this.getCryptor() != null;
+    }
+
+    @Override
+    public boolean contains(final Path file) {
+        if(this.isUnlocked()) {
+            return new SimplePathPredicate(file).test(this.getHome()) || file.isChild(this.getHome());
+        }
+        return false;
+    }
+
+    public abstract String getRegularFileExtension();
+
+    public abstract String getDirectoryMetadataFilename();
+
+    public abstract String getBackupDirectoryMetadataFilename();
+
+    public abstract DirectoryMetadata getRootDirId();
+
+    @Override
+    public synchronized void close() {
+        if(this.isUnlocked()) {
+            if(this.getCryptor() != null) {
+                getCryptor().destroy();
+            }
+            if(this.getDirectoryProvider() != null) {
+                this.getDirectoryProvider().destroy();
+            }
+            if(this.getFilenameProvider() != null) {
+                this.getFilenameProvider().destroy();
+            }
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public  T getFeature(final Session> session, final Class type, final T delegate) {
+        if(this.isUnlocked()) {
+            if(type == ListService.class) {
+                return (T) new CryptoListService(session, (ListService) delegate, this);
+            }
+            if(type == Touch.class) {
+                // Use default touch feature because touch with remote implementation will not add encrypted file header
+                return (T) new CryptoTouchFeature(session, new DefaultTouchFeature(session), this);
+            }
+            if(type == Directory.class) {
+                //TODO
+                return (T) new CryptoDirectoryV7Feature(session, (Directory) delegate, this);
+//                return (T) (this.getVersion() == VAULT_VERSION_DEPRECATED ?
+//                        new CryptoDirectoryV6Feature(session, (Directory) delegate, session._getFeature(Write.class), this) :
+//                        new CryptoDirectoryV7Feature(session, (Directory) delegate, session._getFeature(Write.class), this)
+//                );
+            }
+            if(type == Upload.class) {
+                return (T) new CryptoUploadFeature(session, (Upload) delegate, this);
+            }
+            if(type == Download.class) {
+                return (T) new CryptoDownloadFeature(session, (Download) delegate, session._getFeature(Read.class), this);
+            }
+            if(type == Read.class) {
+                return (T) new CryptoReadFeature(session, (Read) delegate, this);
+            }
+            if(type == Write.class) {
+                return (T) new CryptoWriteFeature(session, (Write) delegate, this);
+            }
+            if(type == MultipartWrite.class) {
+                return (T) new CryptoMultipartWriteFeature(session, (Write) delegate, this);
+            }
+            if(type == Move.class) {
+                //TODO
+                return (T) new CryptoMoveV7Feature(session, (Move) delegate, this);
+//                return (T) (this.getVersion() == VAULT_VERSION_DEPRECATED ?
+//                        new CryptoMoveV6Feature(session, (Move) delegate, this) :
+//                        new CryptoMoveV7Feature(session, (Move) delegate, this));
+
+            }
+            if(type == AttributesFinder.class) {
+                return (T) new CryptoAttributesFeature(session, (AttributesFinder) delegate, this);
+            }
+            if(type == Find.class) {
+                return (T) new CryptoFindFeature(session, (Find) delegate, this);
+            }
+            if(type == UrlProvider.class) {
+                return (T) new CryptoUrlProvider(session, (UrlProvider) delegate, this);
+            }
+            if(type == FileIdProvider.class) {
+                return (T) new CryptoFileIdProvider(session, (FileIdProvider) delegate, this);
+            }
+            if(type == VersionIdProvider.class) {
+                return (T) new CryptoVersionIdProvider(session, (VersionIdProvider) delegate, this);
+            }
+            if(type == Delete.class) {
+                return (T) new CryptoDeleteV7Feature(session, (Delete) delegate, this);
+                //TODO
+//                return (T) (this.getVersion() == VAULT_VERSION_DEPRECATED ?
+//                        new CryptoDeleteV6Feature(session, (Delete) delegate, this) :
+//                        new CryptoDeleteV7Feature(session, (Delete) delegate, this));
+            }
+            if(type == Trash.class) {
+                //TODO
+                return (T) new CryptoDeleteV7Feature(session, (Delete) delegate, this);
+//                return (T) (this.getVersion() == VAULT_VERSION_DEPRECATED ?
+//                        new CryptoDeleteV6Feature(session, (Delete) delegate, this) :
+//                        new CryptoDeleteV7Feature(session, (Delete) delegate, this));
+            }
+            if(type == Symlink.class) {
+                return (T) new CryptoSymlinkFeature(session, (Symlink) delegate, this);
+            }
+            if(type == Headers.class) {
+                return (T) new CryptoHeadersFeature(session, (Headers) delegate, this);
+            }
+            if(type == Compress.class) {
+                return (T) new CryptoCompressFeature(session, (Compress) delegate, this);
+            }
+            if(type == Bulk.class) {
+                return (T) new CryptoBulkFeature(session, (Bulk) delegate, this);
+            }
+            if(type == UnixPermission.class) {
+                return (T) new CryptoUnixPermission(session, (UnixPermission) delegate, this);
+            }
+            if(type == AclPermission.class) {
+                return (T) new CryptoAclPermission(session, (AclPermission) delegate, this);
+            }
+            if(type == Copy.class) {
+                return (T) new CryptoCopyFeature(session, (Copy) delegate, this);
+            }
+            if(type == Timestamp.class) {
+                return (T) new CryptoTimestampFeature(session, (Timestamp) delegate, this);
+            }
+            if(type == Encryption.class) {
+                return (T) new CryptoEncryptionFeature(session, (Encryption) delegate, this);
+            }
+            if(type == Lifecycle.class) {
+                return (T) new CryptoLifecycleFeature(session, (Lifecycle) delegate, this);
+            }
+            if(type == Location.class) {
+                return (T) new CryptoLocationFeature(session, (Location) delegate, this);
+            }
+            if(type == Lock.class) {
+                return (T) new CryptoLockFeature(session, (Lock) delegate, this);
+            }
+            if(type == Logging.class) {
+                return (T) new CryptoLoggingFeature(session, (Logging) delegate, this);
+            }
+            if(type == Redundancy.class) {
+                return (T) new CryptoRedundancyFeature(session, (Redundancy) delegate, this);
+            }
+            if(type == Search.class) {
+                return (T) new CryptoSearchFeature(session, (Search) delegate, this);
+            }
+            if(type == TransferAcceleration.class) {
+                return (T) new CryptoTransferAccelerationFeature<>(session, (TransferAcceleration) delegate, this);
+            }
+            if(type == Versioning.class) {
+                return (T) new CryptoVersioningFeature(session, (Versioning) delegate, this);
+            }
+        }
+        return delegate;
+    }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentReader.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentReader.java
index 21282729888..452af7e12b5 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentReader.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentReader.java
@@ -21,10 +21,12 @@
 import ch.cyberduck.core.Session;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Read;
+import ch.cyberduck.core.io.StreamCopier;
 import ch.cyberduck.core.transfer.TransferStatus;
 
 import org.apache.commons.io.IOUtils;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -49,6 +51,19 @@ public String read(final Path file) throws BackgroundException {
         }
     }
 
+    public byte[] readBytes(final Path file) throws BackgroundException {
+        final Read read = session._getFeature(Read.class);
+        final TransferStatus status = new TransferStatus().setLength(file.attributes().getSize());
+        try (final InputStream in = read.read(file, status, new DisabledConnectionCallback())) {
+            final ByteArrayOutputStream out = new ByteArrayOutputStream();
+            new StreamCopier(status, status).transfer(in, out);
+            return out.toByteArray();
+        }
+        catch(IOException e) {
+            throw new DefaultIOExceptionMappingService().map(e);
+        }
+    }
+
     public Reader getReader(final Path file) throws BackgroundException {
         final Read read = session._getFeature(Read.class);
         return new InputStreamReader(read.read(file, new TransferStatus().setLength(file.attributes().getSize()), new DisabledConnectionCallback()), StandardCharsets.UTF_8);
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java
index 4b31674db9b..7bcac44e2f3 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java
@@ -22,16 +22,15 @@
 import ch.cyberduck.core.features.AclPermission;
 import ch.cyberduck.core.transfer.TransferStatus;
 
-import java.util.EnumSet;
 import java.util.List;
 
 public class CryptoAclPermission implements AclPermission {
 
     private final Session> session;
     private final AclPermission delegate;
-    private final CryptoVault cryptomator;
+    private final AbstractVault cryptomator;
 
-    public CryptoAclPermission(final Session> session, final AclPermission delegate, final CryptoVault cryptomator) {
+    public CryptoAclPermission(final Session> session, final AclPermission delegate, final AbstractVault cryptomator) {
 
         this.session = session;
         this.delegate = delegate;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java
index 88ccb32e736..b61023a58d5 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java
@@ -19,6 +19,8 @@
 import ch.cyberduck.core.Session;
 import ch.cyberduck.core.exception.BackgroundException;
 
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+
 import java.util.EnumSet;
 
 public interface CryptoDirectory {
@@ -27,21 +29,20 @@ public interface CryptoDirectory {
      * Get encrypted filename for given clear text filename with id of parent encrypted directory.
      *
      * @param session     Connection
-     * @param directoryId Parent folder directory id
+     * @param parent      Parent folder
      * @param filename    Clear text filename
      * @param type        File type
      * @return Encrypted filename
      */
-    String toEncrypted(Session> session, String directoryId, String filename, EnumSet type) throws BackgroundException;
+    String toEncrypted(Session> session, Path parent, String filename, EnumSet type) throws BackgroundException;
 
     /**
      * Get encrypted reference for clear text directory path.
      *
-     * @param session     Connection
-     * @param directoryId Directory ID or null to read directory id from metadata file
-     * @param directory   Clear text
+     * @param session   Connection
+     * @param directory Clear text
      */
-    Path toEncrypted(Session> session, String directoryId, Path directory) throws BackgroundException;
+    Path toEncrypted(Session> session, Path directory) throws BackgroundException;
 
     /**
      * Remove from cache
@@ -49,4 +50,8 @@ public interface CryptoDirectory {
     void delete(Path directory);
 
     void destroy();
+
+    DirectoryMetadata getOrCreateDirectoryId(Session> session, Path directory) throws BackgroundException;
+
+    DirectoryMetadata createDirectoryId(final Path directory);
 }
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java
index bcb52f32f5f..8cc455558bc 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java
@@ -27,9 +27,9 @@
 public class CryptoTransferStatus extends ProxyTransferStatus implements StreamCancelation, StreamProgress {
     private static final Logger log = LogManager.getLogger(CryptoTransferStatus.class);
 
-    private final CryptoVault vault;
+    private final AbstractVault vault;
 
-    public CryptoTransferStatus(final CryptoVault vault, final TransferStatus proxy) {
+    public CryptoTransferStatus(final AbstractVault vault, final TransferStatus proxy) {
         super(proxy);
         this.vault = vault;
         this.setLength(vault.toCiphertextSize(proxy.getOffset(), proxy.getLength()))
@@ -42,7 +42,7 @@ public CryptoTransferStatus(final CryptoVault vault, final TransferStatus proxy)
     public TransferStatus setResponse(final PathAttributes attributes) {
         try {
             attributes.setSize(vault.toCleartextSize(0L, attributes.getSize()));
-            attributes.setVault(vault.getHome());
+            attributes.setVaultMetadata(vault.getMetadata());
             super.setResponse(attributes);
         }
         catch(CryptoInvalidFilesizeException e) {
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java
deleted file mode 100644
index 431da0f2ee4..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java
+++ /dev/null
@@ -1,853 +0,0 @@
-package ch.cyberduck.core.cryptomator;
-
-/*
- * Copyright (c) 2002-2016 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.*;
-import ch.cyberduck.core.cryptomator.features.*;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV6Provider;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider;
-import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV6Provider;
-import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider;
-import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.LocalAccessDeniedException;
-import ch.cyberduck.core.exception.LoginCanceledException;
-import ch.cyberduck.core.exception.NotfoundException;
-import ch.cyberduck.core.features.*;
-import ch.cyberduck.core.preferences.Preferences;
-import ch.cyberduck.core.preferences.PreferencesFactory;
-import ch.cyberduck.core.shared.DefaultTouchFeature;
-import ch.cyberduck.core.shared.DefaultUrlProvider;
-import ch.cyberduck.core.transfer.TransferStatus;
-import ch.cyberduck.core.vault.DefaultVaultRegistry;
-import ch.cyberduck.core.vault.VaultCredentials;
-import ch.cyberduck.core.vault.VaultException;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.cryptomator.cryptolib.api.AuthenticationFailedException;
-import org.cryptomator.cryptolib.api.Cryptor;
-import org.cryptomator.cryptolib.api.CryptorProvider;
-import org.cryptomator.cryptolib.api.FileContentCryptor;
-import org.cryptomator.cryptolib.api.FileHeaderCryptor;
-import org.cryptomator.cryptolib.api.InvalidPassphraseException;
-import org.cryptomator.cryptolib.api.Masterkey;
-import org.cryptomator.cryptolib.common.MasterkeyFile;
-import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.text.MessageFormat;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.JWTVerifier;
-import com.auth0.jwt.algorithms.Algorithm;
-import com.auth0.jwt.exceptions.InvalidClaimException;
-import com.auth0.jwt.exceptions.JWTVerificationException;
-import com.auth0.jwt.exceptions.SignatureVerificationException;
-import com.auth0.jwt.interfaces.DecodedJWT;
-import com.google.common.io.BaseEncoding;
-import com.google.gson.JsonParseException;
-
-import static ch.cyberduck.core.vault.DefaultVaultRegistry.DEFAULT_VAULTCONFIG_FILE_NAME;
-
-/**
- * Cryptomator vault implementation
- */
-public class CryptoVault implements Vault {
-    private static final Logger log = LogManager.getLogger(CryptoVault.class);
-
-    public static final int VAULT_VERSION_DEPRECATED = 6;
-    public static final int VAULT_VERSION = PreferencesFactory.get().getInteger("cryptomator.vault.version");
-    public static final byte[] VAULT_PEPPER = PreferencesFactory.get().getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8);
-
-    public static final String DIR_PREFIX = "0";
-
-    private static final Pattern BASE32_PATTERN = Pattern.compile("^0?(([A-Z2-7]{8})*[A-Z2-7=]{8})");
-    private static final Pattern BASE64URL_PATTERN = Pattern.compile("^([A-Za-z0-9_=-]+).c9r");
-
-    private static final String JSON_KEY_VAULTVERSION = "format";
-    private static final String JSON_KEY_CIPHERCONFIG = "cipherCombo";
-    private static final String JSON_KEY_SHORTENING_THRESHOLD = "shorteningThreshold";
-
-    /**
-     * Root of vault directory
-     */
-    private final Path home;
-    private final Path masterkey;
-
-    private final Path config;
-    private final Path vault;
-    private int vaultVersion;
-    private int nonceSize;
-
-    private final PasswordStore keychain = PasswordStoreFactory.get();
-    private final Preferences preferences = PreferencesFactory.get();
-    private Cryptor cryptor;
-    private CryptorCache fileNameCryptor;
-
-    private CryptoFilename filenameProvider;
-    private CryptoDirectory directoryProvider;
-
-    private final byte[] pepper;
-
-    public CryptoVault(final Path home) {
-        this(home, DefaultVaultRegistry.DEFAULT_MASTERKEY_FILE_NAME, DEFAULT_VAULTCONFIG_FILE_NAME, VAULT_PEPPER);
-    }
-
-    public CryptoVault(final Path home, final String masterkey, final String config, final byte[] pepper) {
-        this.home = home;
-        this.masterkey = new Path(home, masterkey, EnumSet.of(Path.Type.file, Path.Type.vault));
-        this.config = new Path(home, config, EnumSet.of(Path.Type.file, Path.Type.vault));
-        this.pepper = pepper;
-        // New vault home with vault flag set for internal use
-        final EnumSet type = EnumSet.copyOf(home.getType());
-        type.add(Path.Type.vault);
-        if(home.isRoot()) {
-            this.vault = new Path(home.getAbsolute(), type, new PathAttributes(home.attributes()));
-        }
-        else {
-            this.vault = new Path(home.getParent(), home.getName(), type, new PathAttributes(home.attributes()));
-        }
-    }
-
-    public synchronized Path create(final Session> session, final VaultCredentials credentials, final int version) throws BackgroundException {
-        return this.create(session, null, credentials, version);
-    }
-
-    public synchronized Path create(final Session> session, final String region, final VaultCredentials credentials, final int version) throws BackgroundException {
-        final Host bookmark = session.getHost();
-        if(credentials.isSaved()) {
-            try {
-                keychain.addPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
-                        new DefaultUrlProvider(bookmark).toUrl(masterkey, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl(), credentials.getPassword());
-            }
-            catch(LocalAccessDeniedException e) {
-                log.error("Failure {} saving credentials for {} in password store", e, bookmark);
-            }
-        }
-        final String passphrase = credentials.getPassword();
-        final ByteArrayOutputStream mkArray = new ByteArrayOutputStream();
-        final Masterkey mk = Masterkey.generate(FastSecureRandomProvider.get().provide());
-        final MasterkeyFileAccess access = new MasterkeyFileAccess(pepper, FastSecureRandomProvider.get().provide());
-        final MasterkeyFile masterkeyFile;
-        try {
-            access.persist(mk, mkArray, passphrase, version);
-            masterkeyFile = MasterkeyFile.read(new StringReader(new String(mkArray.toByteArray(), StandardCharsets.UTF_8)));
-        }
-        catch(IOException e) {
-            throw new VaultException("Failure creating master key", e);
-        }
-        log.debug("Write master key to {}", masterkey);
-        // Obtain non encrypted directory writer
-        final Directory> directory = session._getFeature(Directory.class);
-        final TransferStatus status = new TransferStatus().setRegion(region);
-        final Encryption encryption = session._getFeature(Encryption.class);
-        if(encryption != null) {
-            status.setEncryption(encryption.getDefault(home));
-        }
-        final Path vault = directory.mkdir(session._getFeature(Write.class), home, status);
-        new ContentWriter(session).write(masterkey, mkArray.toByteArray());
-        if(VAULT_VERSION == version) {
-            // Create vaultconfig.cryptomator
-            final Algorithm algorithm = Algorithm.HMAC256(mk.getEncoded());
-            final String conf = JWT.create()
-                    .withJWTId(new UUIDRandomStringService().random())
-                    .withKeyId(String.format("masterkeyfile:%s", masterkey.getName()))
-                    .withClaim(JSON_KEY_VAULTVERSION, version)
-                    .withClaim(JSON_KEY_CIPHERCONFIG, CryptorProvider.Scheme.SIV_GCM.toString())
-                    .withClaim(JSON_KEY_SHORTENING_THRESHOLD, CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD)
-                    .sign(algorithm);
-            new ContentWriter(session).write(config, conf.getBytes(StandardCharsets.US_ASCII));
-            this.open(parseVaultConfigFromJWT(conf).withMasterkeyFile(masterkeyFile), passphrase);
-        }
-        else {
-            this.open(new VaultConfig(version, CryptoFilenameV6Provider.DEFAULT_NAME_SHORTENING_THRESHOLD,
-                    CryptorProvider.Scheme.SIV_CTRMAC, null, null).withMasterkeyFile(masterkeyFile), passphrase);
-        }
-        final Path secondLevel = directoryProvider.toEncrypted(session, home.attributes().getDirectoryId(), home);
-        final Path firstLevel = secondLevel.getParent();
-        final Path dataDir = firstLevel.getParent();
-        log.debug("Create vault root directory at {}", secondLevel);
-        directory.mkdir(session._getFeature(Write.class), dataDir, status);
-        directory.mkdir(session._getFeature(Write.class), firstLevel, status);
-        directory.mkdir(session._getFeature(Write.class), secondLevel, status);
-        return vault;
-    }
-
-    @Override
-    public synchronized Path create(final Session> session, final String region, final VaultCredentials credentials) throws BackgroundException {
-        return this.create(session, region, credentials, VAULT_VERSION);
-    }
-
-    @Override
-    public synchronized CryptoVault load(final Session> session, final PasswordCallback prompt) throws BackgroundException {
-        if(this.isUnlocked()) {
-            log.warn("Skip unlock of open vault {}", this);
-            return this;
-        }
-        final Host bookmark = session.getHost();
-        String passphrase = keychain.getPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
-                new DefaultUrlProvider(bookmark).toUrl(masterkey, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl());
-        if(null == passphrase) {
-            // Legacy
-            passphrase = keychain.getPassword(String.format("Cryptomator Passphrase %s", bookmark.getHostname()),
-                    new DefaultUrlProvider(bookmark).toUrl(masterkey, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl());
-        }
-        return this.unlock(session, prompt, bookmark, passphrase);
-    }
-
-    private VaultConfig readVaultConfig(final Session> session) throws BackgroundException {
-        final MasterkeyFile masterkeyFile = this.readMasterkeyFile(session, masterkey);
-        try {
-            return parseVaultConfigFromJWT(new ContentReader(session).read(config))
-                    .withMasterkeyFile(masterkeyFile);
-        }
-        catch(NotfoundException e) {
-            log.debug("Ignore failure reading vault configuration {}", config);
-            return parseVaultConfigFromMasterKey(masterkeyFile)
-                    .withMasterkeyFile(masterkeyFile);
-        }
-    }
-
-    private static VaultConfig parseVaultConfigFromMasterKey(final MasterkeyFile masterkeyFile) {
-        return new VaultConfig(masterkeyFile.version,
-                masterkeyFile.version == VAULT_VERSION_DEPRECATED ?
-                        CryptoFilenameV6Provider.DEFAULT_NAME_SHORTENING_THRESHOLD :
-                        CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD,
-                CryptorProvider.Scheme.SIV_CTRMAC, null, null);
-    }
-
-
-    private static VaultConfig parseVaultConfigFromJWT(final String token) {
-        final DecodedJWT decoded = JWT.decode(token);
-        return new VaultConfig(
-                decoded.getClaim(JSON_KEY_VAULTVERSION).asInt(),
-                decoded.getClaim(JSON_KEY_SHORTENING_THRESHOLD).asInt(),
-                CryptorProvider.Scheme.valueOf(decoded.getClaim(JSON_KEY_CIPHERCONFIG).asString()),
-                decoded.getAlgorithm(), decoded);
-    }
-
-    private MasterkeyFile readMasterkeyFile(final Session> session, final Path file) throws BackgroundException {
-        log.debug("Read master key {}", file);
-        try(Reader reader = new ContentReader(session).getReader(file)) {
-            return MasterkeyFile.read(reader);
-        }
-        catch(JsonParseException | IllegalArgumentException | IllegalStateException | IOException e) {
-            throw new VaultException(String.format("Failure reading vault master key file %s", file.getName()), e);
-        }
-    }
-
-    public CryptoVault unlock(final Session> session, final PasswordCallback prompt, final Host bookmark, final String passphrase) throws BackgroundException {
-        final VaultConfig vaultConfig = this.readVaultConfig(session);
-        this.unlock(vaultConfig, passphrase, bookmark, prompt,
-                MessageFormat.format(LocaleFactory.localizedString("Provide your passphrase to unlock the Cryptomator Vault {0}", "Cryptomator"), home.getName())
-        );
-        return this;
-    }
-
-    public void unlock(final VaultConfig vaultConfig, final String passphrase, final Host bookmark, final PasswordCallback prompt,
-                       final String message) throws BackgroundException {
-        final Credentials credentials;
-        if(null == passphrase) {
-            credentials = prompt.prompt(
-                    bookmark, LocaleFactory.localizedString("Unlock Vault", "Cryptomator"),
-                    message,
-                    new LoginOptions()
-                            .save(preferences.getBoolean("cryptomator.vault.keychain"))
-                            .user(false)
-                            .anonymous(false)
-                            .icon("cryptomator.tiff")
-                            .passwordPlaceholder(LocaleFactory.localizedString("Passphrase", "Cryptomator")));
-            if(null == credentials.getPassword()) {
-                throw new LoginCanceledException();
-            }
-        }
-        else {
-            credentials = new VaultCredentials(passphrase).withSaved(false);
-        }
-        try {
-            this.open(vaultConfig, credentials.getPassword());
-            if(credentials.isSaved()) {
-                log.info("Save passphrase for {}", masterkey);
-                // Save password with hostname and path to masterkey.cryptomator in keychain
-                keychain.addPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
-                        new DefaultUrlProvider(bookmark).toUrl(masterkey, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl(), credentials.getPassword());
-            }
-        }
-        catch(CryptoAuthenticationException e) {
-            this.unlock(vaultConfig, null, bookmark, prompt, String.format("%s %s.", e.getDetail(),
-                    MessageFormat.format(LocaleFactory.localizedString("Provide your passphrase to unlock the Cryptomator Vault {0}", "Cryptomator"), home.getName())));
-        }
-    }
-
-    @Override
-    public synchronized void close() {
-        if(this.isUnlocked()) {
-            log.info("Close vault with cryptor {}", cryptor);
-            if(cryptor != null) {
-                cryptor.destroy();
-            }
-            if(directoryProvider != null) {
-                directoryProvider.destroy();
-            }
-            if(filenameProvider != null) {
-                filenameProvider.destroy();
-            }
-        }
-        cryptor = null;
-        fileNameCryptor = null;
-    }
-
-    protected CryptoFilename createFilenameProvider(final VaultConfig vaultConfig) {
-        switch(vaultConfig.version) {
-            case VAULT_VERSION_DEPRECATED:
-                return new CryptoFilenameV6Provider(vault);
-            default:
-                return new CryptoFilenameV7Provider(vaultConfig.getShorteningThreshold());
-        }
-    }
-
-    protected CryptoDirectory createDirectoryProvider(final VaultConfig vaultConfig) {
-        switch(vaultConfig.version) {
-            case VAULT_VERSION_DEPRECATED:
-                return new CryptoDirectoryV6Provider(vault, this);
-            default:
-                return new CryptoDirectoryV7Provider(vault, this);
-        }
-    }
-
-    protected void open(final VaultConfig vaultConfig, final CharSequence passphrase) throws BackgroundException {
-        this.open(vaultConfig, passphrase, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig));
-    }
-
-    protected void open(final VaultConfig vaultConfig, final CharSequence passphrase,
-                        final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException {
-        try {
-            final Masterkey masterKey = this.getMasterKey(vaultConfig.getMkfile(), passphrase);
-            this.open(vaultConfig, masterKey, filenameProvider, directoryProvider);
-        }
-        catch(IllegalArgumentException | IOException e) {
-            throw new VaultException("Failure reading key file", e);
-        }
-        catch(InvalidPassphraseException e) {
-            throw new CryptoAuthenticationException("Failure to decrypt master key file", e);
-        }
-    }
-
-    protected void open(final VaultConfig vaultConfig, final Masterkey masterKey) throws BackgroundException {
-        this.open(vaultConfig, masterKey, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig));
-    }
-
-    protected void open(final VaultConfig vaultConfig, final Masterkey masterKey,
-                        final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException {
-        this.vaultVersion = vaultConfig.version;
-        final CryptorProvider provider = CryptorProvider.forScheme(vaultConfig.getCipherCombo());
-        log.debug("Initialized crypto provider {}", provider);
-        vaultConfig.verify(masterKey.getEncoded(), VAULT_VERSION);
-        this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide());
-        this.fileNameCryptor = new CryptorCache(cryptor.fileNameCryptor());
-        this.filenameProvider = filenameProvider;
-        this.directoryProvider = directoryProvider;
-        this.nonceSize = vaultConfig.getNonceSize();
-    }
-
-    private Masterkey getMasterKey(final MasterkeyFile mkFile, final CharSequence passphrase) throws IOException {
-        final StringWriter writer = new StringWriter();
-        mkFile.write(writer);
-        return new MasterkeyFileAccess(pepper, FastSecureRandomProvider.get().provide()).load(
-                new ByteArrayInputStream(writer.getBuffer().toString().getBytes(StandardCharsets.UTF_8)), passphrase);
-    }
-
-    public synchronized boolean isUnlocked() {
-        return cryptor != null;
-    }
-
-    @Override
-    public State getState() {
-        return this.isUnlocked() ? State.open : State.closed;
-    }
-
-    @Override
-    public boolean contains(final Path file) {
-        return new SimplePathPredicate(file).test(home) || file.isChild(home);
-    }
-
-    @Override
-    public Path encrypt(final Session> session, final Path file) throws BackgroundException {
-        return this.encrypt(session, file, file.attributes().getDirectoryId(), false);
-    }
-
-    @Override
-    public Path encrypt(final Session> session, final Path file, boolean metadata) throws BackgroundException {
-        return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata);
-    }
-
-    public Path encrypt(final Session> session, final Path file, final String directoryId, boolean metadata) throws BackgroundException {
-        final Path encrypted;
-        if(file.isFile() || metadata) {
-            if(file.getType().contains(Path.Type.vault)) {
-                log.warn("Skip file {} because it is marked as an internal vault path", file);
-                return file;
-            }
-            if(new SimplePathPredicate(file).test(home)) {
-                log.warn("Skip vault home {} because the root has no metadata file", file);
-                return file;
-            }
-            final Path parent;
-            final String filename;
-            if(file.getType().contains(Path.Type.encrypted)) {
-                final Path decrypted = file.attributes().getDecrypted();
-                parent = directoryProvider.toEncrypted(session, decrypted.getParent().attributes().getDirectoryId(), decrypted.getParent());
-                filename = directoryProvider.toEncrypted(session, parent.attributes().getDirectoryId(), decrypted.getName(), decrypted.getType());
-            }
-            else {
-                parent = directoryProvider.toEncrypted(session, file.getParent().attributes().getDirectoryId(), file.getParent());
-                filename = directoryProvider.toEncrypted(session, parent.attributes().getDirectoryId(), file.getName(), file.getType());
-            }
-            final PathAttributes attributes = new PathAttributes(file.attributes());
-            attributes.setDirectoryId(null);
-            if(!file.isFile() && !metadata) {
-                // The directory is different from the metadata file used to resolve the actual folder
-                attributes.setVersionId(null);
-                attributes.setFileId(null);
-            }
-            // Translate file size
-            attributes.setSize(this.toCiphertextSize(0L, file.attributes().getSize()));
-            final EnumSet type = EnumSet.copyOf(file.getType());
-            if(metadata && vaultVersion == VAULT_VERSION_DEPRECATED) {
-                type.remove(Path.Type.directory);
-                type.add(Path.Type.file);
-            }
-            type.remove(Path.Type.decrypted);
-            type.add(Path.Type.encrypted);
-            encrypted = new Path(parent, filename, type, attributes);
-        }
-        else {
-            if(file.getType().contains(Path.Type.encrypted)) {
-                log.warn("Skip file {} because it is already marked as an encrypted path", file);
-                return file;
-            }
-            if(file.getType().contains(Path.Type.vault)) {
-                return directoryProvider.toEncrypted(session, home.attributes().getDirectoryId(), home);
-            }
-            encrypted = directoryProvider.toEncrypted(session, directoryId, file);
-        }
-        // Add reference to decrypted file
-        if(!file.getType().contains(Path.Type.encrypted)) {
-            encrypted.attributes().setDecrypted(file);
-        }
-        // Add reference for vault
-        file.attributes().setVault(home);
-        encrypted.attributes().setVault(home);
-        return encrypted;
-    }
-
-    @Override
-    public Path decrypt(final Session> session, final Path file) throws BackgroundException {
-        if(file.getType().contains(Path.Type.decrypted)) {
-            log.warn("Skip file {} because it is already marked as an decrypted path", file);
-            return file;
-        }
-        if(file.getType().contains(Path.Type.vault)) {
-            log.warn("Skip file {} because it is marked as an internal vault path", file);
-            return file;
-        }
-        final Path inflated = this.inflate(session, file);
-        final Pattern pattern = vaultVersion == VAULT_VERSION_DEPRECATED ? BASE32_PATTERN : BASE64URL_PATTERN;
-        final Matcher m = pattern.matcher(inflated.getName());
-        if(m.matches()) {
-            final String ciphertext = m.group(1);
-            try {
-                final String cleartextFilename = fileNameCryptor.decryptFilename(
-                        vaultVersion == VAULT_VERSION_DEPRECATED ? BaseEncoding.base32() : BaseEncoding.base64Url(),
-                        ciphertext, file.getParent().attributes().getDirectoryId().getBytes(StandardCharsets.UTF_8));
-                final PathAttributes attributes = new PathAttributes(file.attributes());
-                if(this.isDirectory(inflated)) {
-                    if(Permission.EMPTY != attributes.getPermission()) {
-                        final Permission permission = new Permission(attributes.getPermission());
-                        permission.setUser(permission.getUser().or(Permission.Action.execute));
-                        permission.setGroup(permission.getGroup().or(Permission.Action.execute));
-                        permission.setOther(permission.getOther().or(Permission.Action.execute));
-                        attributes.setPermission(permission);
-                    }
-                    // Reset size for folders
-                    attributes.setSize(-1L);
-                    attributes.setVersionId(null);
-                    attributes.setFileId(null);
-                }
-                else {
-                    // Translate file size
-                    attributes.setSize(this.toCleartextSize(0L, file.attributes().getSize()));
-                }
-                // Add reference to encrypted file
-                attributes.setEncrypted(file);
-                // Add reference for vault
-                attributes.setVault(home);
-                final EnumSet type = EnumSet.copyOf(file.getType());
-                type.remove(this.isDirectory(inflated) ? Path.Type.file : Path.Type.directory);
-                type.add(this.isDirectory(inflated) ? Path.Type.directory : Path.Type.file);
-                type.remove(Path.Type.encrypted);
-                type.add(Path.Type.decrypted);
-                final Path decrypted = new Path(file.getParent().attributes().getDecrypted(), cleartextFilename, type, attributes);
-                if(type.contains(Path.Type.symboliclink)) {
-                    decrypted.setSymlinkTarget(file.getSymlinkTarget());
-                }
-                return decrypted;
-            }
-            catch(AuthenticationFailedException e) {
-                throw new CryptoAuthenticationException(
-                        "Failure to decrypt due to an unauthentic ciphertext", e);
-            }
-        }
-        else {
-            throw new CryptoFilenameMismatchException(
-                    String.format("Failure to decrypt %s due to missing pattern match for %s", inflated.getName(), pattern));
-        }
-    }
-
-    private boolean isDirectory(final Path p) {
-        if(vaultVersion == VAULT_VERSION_DEPRECATED) {
-            return p.getName().startsWith(DIR_PREFIX);
-        }
-        return p.isDirectory();
-    }
-
-    @Override
-    public long toCiphertextSize(final long cleartextFileOffset, final long cleartextFileSize) {
-        if(TransferStatus.UNKNOWN_LENGTH == cleartextFileSize) {
-            return TransferStatus.UNKNOWN_LENGTH;
-        }
-        final int headerSize;
-        if(0L == cleartextFileOffset) {
-            headerSize = cryptor.fileHeaderCryptor().headerSize();
-        }
-        else {
-            headerSize = 0;
-        }
-        return headerSize + cryptor.fileContentCryptor().ciphertextSize(cleartextFileSize);
-    }
-
-    @Override
-    public long toCleartextSize(final long cleartextFileOffset, final long ciphertextFileSize) throws CryptoInvalidFilesizeException {
-        if(TransferStatus.UNKNOWN_LENGTH == ciphertextFileSize) {
-            return TransferStatus.UNKNOWN_LENGTH;
-        }
-        final int headerSize;
-        if(0L == cleartextFileOffset) {
-            headerSize = cryptor.fileHeaderCryptor().headerSize();
-        }
-        else {
-            headerSize = 0;
-        }
-        try {
-            return cryptor.fileContentCryptor().cleartextSize(ciphertextFileSize - headerSize);
-        }
-        catch(AssertionError e) {
-            throw new CryptoInvalidFilesizeException(String.format("Encrypted file size must be at least %d bytes", headerSize));
-        }
-        catch(IllegalArgumentException e) {
-            throw new CryptoInvalidFilesizeException(String.format("Invalid file size. %s", e.getMessage()));
-        }
-    }
-
-    private Path inflate(final Session> session, final Path file) throws BackgroundException {
-        final String fileName = file.getName();
-        if(filenameProvider.isDeflated(fileName)) {
-            final String filename = filenameProvider.inflate(session, fileName);
-            return new Path(file.getParent(), filename, EnumSet.of(Path.Type.file), file.attributes());
-        }
-        return file;
-    }
-
-    public Path getHome() {
-        return home;
-    }
-
-    public Path getMasterkey() {
-        return masterkey;
-    }
-
-    public Path getConfig() {
-        return config;
-    }
-
-    public FileHeaderCryptor getFileHeaderCryptor() {
-        return cryptor.fileHeaderCryptor();
-    }
-
-    public FileContentCryptor getFileContentCryptor() {
-        return cryptor.fileContentCryptor();
-    }
-
-    public CryptorCache getFileNameCryptor() {
-        return fileNameCryptor;
-    }
-
-    public CryptoFilename getFilenameProvider() {
-        return filenameProvider;
-    }
-
-    public CryptoDirectory getDirectoryProvider() {
-        return directoryProvider;
-    }
-
-    public int getNonceSize() {
-        return nonceSize;
-    }
-
-    public int numberOfChunks(final long cleartextFileSize) {
-        return (int) (cleartextFileSize / cryptor.fileContentCryptor().cleartextChunkSize() +
-                ((cleartextFileSize % cryptor.fileContentCryptor().cleartextChunkSize() > 0) ? 1 : 0));
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public  T getFeature(final Session> session, final Class type, final T delegate) {
-        if(this.isUnlocked()) {
-            if(type == ListService.class) {
-                return (T) new CryptoListService(session, (ListService) delegate, this);
-            }
-            if(type == Touch.class) {
-                // Use default touch feature because touch with remote implementation will not add encrypted file header
-                return (T) new CryptoTouchFeature(session, new DefaultTouchFeature(session), this);
-            }
-            if(type == Directory.class) {
-                return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ?
-                        new CryptoDirectoryV6Feature(session, (Directory) delegate, this) :
-                        new CryptoDirectoryV7Feature(session, (Directory) delegate, this)
-                );
-            }
-            if(type == Upload.class) {
-                return (T) new CryptoUploadFeature(session, (Upload) delegate, this);
-            }
-            if(type == Download.class) {
-                return (T) new CryptoDownloadFeature(session, (Download) delegate, this);
-            }
-            if(type == Read.class) {
-                return (T) new CryptoReadFeature(session, (Read) delegate, this);
-            }
-            if(type == Write.class) {
-                return (T) new CryptoWriteFeature(session, (Write) delegate, this);
-            }
-            if(type == MultipartWrite.class) {
-                return (T) new CryptoMultipartWriteFeature(session, (Write) delegate, this);
-            }
-            if(type == Move.class) {
-                return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ?
-                        new CryptoMoveV6Feature(session, (Move) delegate, this) :
-                        new CryptoMoveV7Feature(session, (Move) delegate, this));
-
-            }
-            if(type == AttributesFinder.class) {
-                return (T) new CryptoAttributesFeature(session, (AttributesFinder) delegate, this);
-            }
-            if(type == Find.class) {
-                return (T) new CryptoFindFeature(session, (Find) delegate, this);
-            }
-            if(type == UrlProvider.class) {
-                return (T) new CryptoUrlProvider(session, (UrlProvider) delegate, this);
-            }
-            if(type == FileIdProvider.class) {
-                return (T) new CryptoFileIdProvider(session, (FileIdProvider) delegate, this);
-            }
-            if(type == VersionIdProvider.class) {
-                return (T) new CryptoVersionIdProvider(session, (VersionIdProvider) delegate, this);
-            }
-            if(type == Delete.class) {
-                return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ?
-                        new CryptoDeleteV6Feature(session, (Delete) delegate, this) :
-                        new CryptoDeleteV7Feature(session, (Delete) delegate, this));
-            }
-            if(type == Trash.class) {
-                return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ?
-                        new CryptoDeleteV6Feature(session, (Delete) delegate, this) :
-                        new CryptoDeleteV7Feature(session, (Delete) delegate, this));
-            }
-            if(type == Symlink.class) {
-                return (T) new CryptoSymlinkFeature(session, (Symlink) delegate, this);
-            }
-            if(type == Headers.class) {
-                return (T) new CryptoHeadersFeature(session, (Headers) delegate, this);
-            }
-            if(type == Compress.class) {
-                return (T) new CryptoCompressFeature(session, (Compress) delegate, this);
-            }
-            if(type == Bulk.class) {
-                return (T) new CryptoBulkFeature(session, (Bulk) delegate, this);
-            }
-            if(type == UnixPermission.class) {
-                return (T) new CryptoUnixPermission(session, (UnixPermission) delegate, this);
-            }
-            if(type == AclPermission.class) {
-                return (T) new CryptoAclPermission(session, (AclPermission) delegate, this);
-            }
-            if(type == Copy.class) {
-                return (T) new CryptoCopyFeature(session, (Copy) delegate, this);
-            }
-            if(type == Timestamp.class) {
-                return (T) new CryptoTimestampFeature(session, (Timestamp) delegate, this);
-            }
-            if(type == Encryption.class) {
-                return (T) new CryptoEncryptionFeature(session, (Encryption) delegate, this);
-            }
-            if(type == Lifecycle.class) {
-                return (T) new CryptoLifecycleFeature(session, (Lifecycle) delegate, this);
-            }
-            if(type == Location.class) {
-                return (T) new CryptoLocationFeature(session, (Location) delegate, this);
-            }
-            if(type == Lock.class) {
-                return (T) new CryptoLockFeature(session, (Lock) delegate, this);
-            }
-            if(type == Logging.class) {
-                return (T) new CryptoLoggingFeature(session, (Logging) delegate, this);
-            }
-            if(type == Redundancy.class) {
-                return (T) new CryptoRedundancyFeature(session, (Redundancy) delegate, this);
-            }
-            if(type == Search.class) {
-                return (T) new CryptoSearchFeature(session, (Search) delegate, this);
-            }
-            if(type == TransferAcceleration.class) {
-                return (T) new CryptoTransferAccelerationFeature<>(session, (TransferAcceleration) delegate, this);
-            }
-            if(type == Versioning.class) {
-                return (T) new CryptoVersioningFeature(session, (Versioning) delegate, this);
-            }
-        }
-        return delegate;
-    }
-
-    @Override
-    public boolean equals(final Object o) {
-        if(this == o) {
-            return true;
-        }
-        if(!(o instanceof CryptoVault)) {
-            return false;
-        }
-        final CryptoVault that = (CryptoVault) o;
-        return new SimplePathPredicate(home).test(that.home);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(new SimplePathPredicate(home));
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder("CryptoVault{");
-        sb.append("home=").append(home);
-        sb.append(", cryptor=").append(cryptor);
-        sb.append('}');
-        return sb.toString();
-    }
-
-    public static class VaultConfig {
-
-        private final int version;
-        private final int shorteningThreshold;
-        private final CryptorProvider.Scheme cipherCombo;
-        private final String algorithm;
-        private final DecodedJWT token;
-        private MasterkeyFile mkfile;
-
-        public VaultConfig(int version, int shorteningThreshold, CryptorProvider.Scheme cipherCombo, String algorithm, DecodedJWT token) {
-            this.version = version;
-            this.shorteningThreshold = shorteningThreshold;
-            this.cipherCombo = cipherCombo;
-            this.algorithm = algorithm;
-            this.token = token;
-        }
-
-        public int vaultVersion() {
-            return version;
-        }
-
-        public VaultConfig withMasterkeyFile(final MasterkeyFile mkfile) {
-            this.mkfile = mkfile;
-            return this;
-        }
-
-        public MasterkeyFile getMkfile() {
-            return mkfile;
-        }
-
-        public int getShorteningThreshold() {
-            return shorteningThreshold;
-        }
-
-        public CryptorProvider.Scheme getCipherCombo() {
-            return cipherCombo;
-        }
-
-        public int getNonceSize() throws VaultException {
-            switch(cipherCombo) {
-                case SIV_CTRMAC:
-                    return 16;
-                case SIV_GCM:
-                    return 12;
-                default:
-                    throw new VaultException(String.format("Unsupported cipher scheme %s", cipherCombo));
-            }
-        }
-
-        private Algorithm initAlgorithm(byte[] rawKey) throws VaultException {
-            switch(algorithm) {
-                case "HS256":
-                    return Algorithm.HMAC256(rawKey);
-                case "HS384":
-                    return Algorithm.HMAC384(rawKey);
-                case "HS512":
-                    return Algorithm.HMAC512(rawKey);
-                default:
-                    throw new VaultException(String.format("Unsupported signature algorithm %s", algorithm));
-            }
-        }
-
-        public void verify(byte[] rawKey, int expectedVaultVersion) throws VaultException {
-            try {
-                if(token == null) {
-                    return;
-                }
-                JWTVerifier verifier = JWT.require(initAlgorithm(rawKey))
-                        .withClaim(JSON_KEY_VAULTVERSION, expectedVaultVersion)
-                        .build();
-                verifier.verify(token);
-            }
-            catch(SignatureVerificationException e) {
-                throw new VaultException("Invalid JWT signature", e);
-            }
-            catch(InvalidClaimException e) {
-                throw new VaultException(String.format("Expected vault config for version %d", expectedVaultVersion), e);
-            }
-            catch(JWTVerificationException e) {
-                throw new VaultException(String.format("Failed to verify vault config %s", token), e);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultProvider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultProvider.java
new file mode 100644
index 00000000000..aff3414203e
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultProvider.java
@@ -0,0 +1,107 @@
+package ch.cyberduck.core.cryptomator;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.ListProgressListener;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.features.Find;
+import ch.cyberduck.core.preferences.HostPreferencesFactory;
+import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultProvider;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.EnumSet;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Cryptomator vault implementation
+ */
+public class CryptoVaultProvider implements VaultProvider {
+    private static final Logger log = LogManager.getLogger(CryptoVaultProvider.class);
+
+    private final Map markers;
+
+    public CryptoVaultProvider(final Session> session) {
+        this.markers = ImmutableMap.of(
+                HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"), VaultMetadata.Type.V8,
+                HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename.uvf"), VaultMetadata.Type.UVF
+        );
+    }
+
+    @Override
+    public boolean isVault(final Path path) {
+        return markers.keySet().stream().anyMatch(marker -> path.getName().equals(marker));
+    }
+
+    @Override
+    public VaultMetadata metadata(final Path path) {
+        if(this.isVault(path)) {
+            return new VaultMetadata(path.getParent(), markers.get(path.getName()));
+        }
+        return null;
+    }
+
+    @Override
+    public VaultMetadata find(final Path directory, final Find find, final ListProgressListener listener) throws BackgroundException {
+        for(String marker : markers.keySet()) {
+            final Path m = new Path(directory, marker, EnumSet.of(Path.Type.file));
+            if(find.find(m, listener)) {
+                return new VaultMetadata(m.getParent(), markers.get(marker));
+            }
+        }
+        return null;
+    }
+
+    //TODO prompt parameter wegnehmen?
+    @Override
+    public synchronized AbstractVault provide(final Session> session, final VaultMetadata metadata) {
+        switch(metadata.type) {
+            case V8:
+                return new ch.cyberduck.core.cryptomator.impl.v8.CryptoVault(metadata.root);
+            case UVF:
+                return new ch.cyberduck.core.cryptomator.impl.uvf.CryptoVault(metadata.root);
+            default:
+                log.error("Unknown vault type {}", metadata.type);
+                // TODO schmeissen, DISABLED zurück geben geht nicht weil kein AbstractVault
+                // throw new ...
+                return null;
+        }
+    }
+
+    //TODO create methode braucht es glaube ich nicht unbedingt
+
+    @Override
+    public AbstractVault create(final Session> session, final String region, final VaultCredentials credentials, final VaultMetadata metadata) throws BackgroundException {
+        switch(metadata.type) {
+            case V8:
+                return new ch.cyberduck.core.cryptomator.impl.v8.CryptoVault(metadata.root).create(session, region, credentials);
+            case UVF:
+                return new ch.cyberduck.core.cryptomator.impl.uvf.CryptoVault(metadata.root).create(session, region, credentials);
+            default:
+                log.error("Unknown vault type {}", metadata.type);
+                // TODO schmeissen, DISABLED zurück geben geht nicht weil kein AbstractVault
+                // throw new ...
+                return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptorCache.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptorCache.java
index f62cf502146..96bc1893bbf 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptorCache.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptorCache.java
@@ -20,6 +20,7 @@
 import org.cryptomator.cryptolib.api.AuthenticationFailedException;
 import org.cryptomator.cryptolib.api.FileNameCryptor;
 
+import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -29,7 +30,7 @@ public class CryptorCache {
 
     public static final BaseEncoding BASE32 = BaseEncoding.base32();
 
-    private final LRUCache directoryIdCache = LRUCache.build(250);
+    private final LRUCache directoryIdCache = LRUCache.build(250);
     private final LRUCache decryptCache = LRUCache.build(5000);
     private final LRUCache encryptCache = LRUCache.build(5000);
 
@@ -39,11 +40,12 @@ public CryptorCache(final FileNameCryptor impl) {
         this.impl = impl;
     }
 
-    public String hashDirectoryId(final String cleartextDirectoryId) {
-        if(!directoryIdCache.contains(cleartextDirectoryId)) {
-            directoryIdCache.put(cleartextDirectoryId, impl.hashDirectoryId(cleartextDirectoryId));
+    public String hashDirectoryId(final byte[] cleartextDirectoryId) {
+        final ByteBuffer wrap = ByteBuffer.wrap(cleartextDirectoryId);
+        if(!directoryIdCache.contains(wrap)) {
+            directoryIdCache.put(wrap, impl.hashDirectoryId(cleartextDirectoryId));
         }
-        return directoryIdCache.get(cleartextDirectoryId);
+        return directoryIdCache.get(wrap);
     }
 
     public String encryptFilename(final BaseEncoding encoding, final String cleartextName, final byte[] associatedData) {
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java
index 5276d3e3d03..3b66ca2b21a 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java
@@ -51,7 +51,7 @@ public PathAttributes find(final Path file, final ListProgressListener listener)
         if(file.isDirectory()) {
             attributes.setSize(-1L);
         }
-        attributes.setVault(vault.getHome());
+        attributes.setVaultMetadata(vault.getMetadata());
         return attributes;
     }
 
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java
index 90f7be910c4..2aa4f98ff72 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java
@@ -21,7 +21,7 @@
 import ch.cyberduck.core.RandomStringService;
 import ch.cyberduck.core.Session;
 import ch.cyberduck.core.UUIDRandomStringService;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
 import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator;
 import ch.cyberduck.core.exception.BackgroundException;
@@ -45,9 +45,9 @@ public class CryptoBulkFeature implements Bulk {
 
     private final Session> session;
     private final Bulk delegate;
-    private final CryptoVault cryptomator;
+    private final AbstractVault cryptomator;
 
-    public CryptoBulkFeature(final Session> session, final Bulk delegate, final CryptoVault cryptomator) {
+    public CryptoBulkFeature(final Session> session, final Bulk delegate, final AbstractVault cryptomator) {
         this.session = session;
         this.delegate = delegate;
         this.cryptomator = cryptomator;
@@ -82,9 +82,8 @@ public int compare(final Map.Entry o1, final Map.E
                 if(!status.isExists()) {
                     switch(type) {
                         case upload:
-                            // Preset directory ID for new folders to avert lookup with not found failure in directory ID provider
-                            final String directoryId = random.random();
-                            encrypted.put(new TransferItem(cryptomator.encrypt(session, file, directoryId, false), local), status);
+                            cryptomator.getDirectoryProvider().createDirectoryId(file);
+                            encrypted.put(new TransferItem(cryptomator.encrypt(session, file, false), local), status);
                             break;
                         default:
                             encrypted.put(new TransferItem(cryptomator.encrypt(session, file), local), status);
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoChecksumCompute.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoChecksumCompute.java
index c591b99d69c..41ae969b7fd 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoChecksumCompute.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoChecksumCompute.java
@@ -15,8 +15,8 @@
  * GNU General Public License for more details.
  */
 
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.CryptoOutputStream;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
 import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator;
 import ch.cyberduck.core.exception.BackgroundException;
@@ -55,11 +55,11 @@
 public class CryptoChecksumCompute extends AbstractChecksumCompute {
     private static final Logger log = LogManager.getLogger(CryptoChecksumCompute.class);
 
-    private final CryptoVault cryptomator;
+    private final AbstractVault cryptomator;
     private final ChecksumCompute delegate;
 
-    public CryptoChecksumCompute(final ChecksumCompute delegate, final CryptoVault vault) {
-        this.cryptomator = vault;
+    public CryptoChecksumCompute(final ChecksumCompute delegate, final AbstractVault CryptoVaultInterface) {
+        this.cryptomator = CryptoVaultInterface;
         this.delegate = delegate;
     }
 
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java
index 83d536d17b2..cc172478978 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java
@@ -19,7 +19,7 @@
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.PathAttributes;
 import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
 import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator;
 import ch.cyberduck.core.exception.BackgroundException;
@@ -37,11 +37,11 @@ public class CryptoCopyFeature implements Copy {
 
     private final Session> session;
     private final Copy proxy;
-    private final CryptoVault vault;
+    private final AbstractVault vault;
 
     private Session> target;
 
-    public CryptoCopyFeature(final Session> session, final Copy proxy, final CryptoVault vault) {
+    public CryptoCopyFeature(final Session> session, final Copy proxy, final AbstractVault vault) {
         this.session = session;
         this.target = session;
         this.proxy = proxy;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java
deleted file mode 100644
index cbdda42cf13..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package ch.cyberduck.core.cryptomator.features;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.DisabledListProgressListener;
-import ch.cyberduck.core.ListService;
-import ch.cyberduck.core.PasswordCallback;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoFilename;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.exception.AccessDeniedException;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.NotfoundException;
-import ch.cyberduck.core.features.Delete;
-import ch.cyberduck.core.features.Find;
-import ch.cyberduck.core.features.Trash;
-import ch.cyberduck.core.transfer.TransferStatus;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-
-public class CryptoDeleteV6Feature implements Delete, Trash {
-    private static final Logger log = LogManager.getLogger(CryptoDeleteV6Feature.class);
-
-    private final Session> session;
-    private final Delete proxy;
-    private final CryptoVault vault;
-    private final CryptoFilename filenameProvider;
-
-    public CryptoDeleteV6Feature(final Session> session, final Delete proxy, final CryptoVault vault) {
-        this.session = session;
-        this.proxy = proxy;
-        this.vault = vault;
-        this.filenameProvider = vault.getFilenameProvider();
-    }
-
-    @Override
-    public void delete(final Map files, final PasswordCallback prompt, final Callback callback) throws BackgroundException {
-        for(Path f : files.keySet()) {
-            final List metadataFiles = new ArrayList<>();
-            if(!f.equals(vault.getHome())) {
-                final Path encrypt = vault.encrypt(session, f);
-                try {
-                    proxy.delete(Collections.singletonList(encrypt), prompt, callback);
-                }
-                catch(NotfoundException | AccessDeniedException e) {
-                    if(f.isDirectory()) {
-                        log.error("Failure {} deleting directory {}", e, encrypt);
-                    }
-                    else {
-                        throw e;
-                    }
-                }
-                final Path metadata = vault.encrypt(session, f, true);
-                if(f.isDirectory()) {
-                    // Delete metadata file for directory
-                    log.debug("Add metadata file {}", metadata);
-                    metadataFiles.add(metadata);
-                    vault.getDirectoryProvider().delete(f);
-                }
-                if(filenameProvider.isDeflated(metadata.getName())) {
-                    filenameProvider.invalidate(filenameProvider.inflate(session, metadata.getName()));
-                    final Path metadataFile = filenameProvider.resolve(metadata.getName());
-                    log.debug("Add metadata file {}", metadata);
-                    metadataFiles.add(metadataFile);
-                }
-            }
-            if(!metadataFiles.isEmpty()) {
-                proxy.delete(metadataFiles, prompt, callback);
-            }
-        }
-        for(Path f : files.keySet()) {
-            if(f.equals(vault.getHome())) {
-                log.warn("Recursively delete vault {}", f);
-                final List metadata = new ArrayList<>();
-                if(!proxy.features(f).contains(Delete.Flags.recursive)) {
-                    final Find find = session._getFeature(Find.class);
-                    final Path dataRoot = new Path(f, "d", f.getType());
-                    if(find.find(dataRoot)) {
-                        for(Path d : session._getFeature(ListService.class).list(dataRoot, new DisabledListProgressListener()).toList()) {
-                            metadata.addAll(session._getFeature(ListService.class).list(d, new DisabledListProgressListener()).toList());
-                            metadata.add(d);
-                        }
-                        metadata.add(dataRoot);
-                    }
-                    final Path metaRoot = new Path(f, "m", f.getType());
-                    if(find.find(metaRoot)) {
-                        for(Path m : session._getFeature(ListService.class).list(metaRoot, new DisabledListProgressListener()).toList()) {
-                            for(Path m2 : session._getFeature(ListService.class).list(m, new DisabledListProgressListener()).toList()) {
-                                metadata.addAll(session._getFeature(ListService.class).list(m2, new DisabledListProgressListener()).toList());
-                                metadata.add(m2);
-                            }
-                            metadata.add(m);
-                        }
-                        metadata.add(metaRoot);
-                    }
-                    metadata.add(vault.getMasterkey());
-                }
-                metadata.add(f);
-                proxy.delete(metadata, prompt, callback);
-            }
-        }
-    }
-
-    @Override
-    public void preflight(final Path file) throws BackgroundException {
-        proxy.preflight(vault.encrypt(session, file));
-    }
-
-    @Override
-    public EnumSet features(final Path file) {
-        return proxy.features(file);
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder("CryptoDeleteV6Feature{");
-        sb.append("proxy=").append(proxy);
-        sb.append('}');
-        return sb.toString();
-    }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java
index ab1d6b34b73..1c8c02c26bd 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java
@@ -15,14 +15,14 @@
  * GNU General Public License for more details.
  */
 
+import ch.cyberduck.core.AbstractPath;
 import ch.cyberduck.core.DisabledListProgressListener;
 import ch.cyberduck.core.ListService;
 import ch.cyberduck.core.PasswordCallback;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.CryptoFilename;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider;
 import ch.cyberduck.core.exception.AccessDeniedException;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.exception.NotfoundException;
@@ -45,10 +45,10 @@ public class CryptoDeleteV7Feature implements Delete, Trash {
 
     private final Session> session;
     private final Delete proxy;
-    private final CryptoVault vault;
+    private final AbstractVault vault;
     private final CryptoFilename filenameProvider;
 
-    public CryptoDeleteV7Feature(final Session> session, final Delete proxy, final CryptoVault vault) {
+    public CryptoDeleteV7Feature(final Session> session, final Delete proxy, final AbstractVault vault) {
         this.session = session;
         this.proxy = proxy;
         this.vault = vault;
@@ -62,8 +62,8 @@ public void delete(final Map files, final PasswordCallback
             if(!f.equals(vault.getHome())) {
                 final Path encrypt = vault.encrypt(session, f);
                 if(f.isDirectory()) {
-                    final Path backup = new Path(encrypt, CryptoDirectoryV7Provider.BACKUP_DIRECTORY_METADATAFILE,
-                            EnumSet.of(Path.Type.file));
+                    final Path backup = new Path(encrypt, vault.getBackupDirectoryMetadataFilename(),
+                            EnumSet.of(AbstractPath.Type.file));
                     try {
                         log.debug("Deleting directory id backup file {}", backup);
                         proxy.delete(Collections.singletonList(backup), prompt, callback);
@@ -87,7 +87,7 @@ public void delete(final Map files, final PasswordCallback
                 }
                 final Path metadata = vault.encrypt(session, f, true);
                 if(f.isDirectory()) {
-                    final Path metadataFile = new Path(metadata, CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, EnumSet.of(Path.Type.file));
+                    final Path metadataFile = new Path(metadata, vault.getDirectoryMetadataFilename(), EnumSet.of(Path.Type.file));
                     log.debug("Add metadata file {}", metadataFile);
                     metadataFiles.add(metadataFile);
                     metadataFiles.add(metadata);
@@ -124,8 +124,8 @@ public void delete(final Map files, final PasswordCallback
                         }
                         metadata.add(dataRoot);
                     }
-                    if(vault.getMasterkey() != null) {
-                        metadata.add(vault.getMasterkey());
+                    if(vault.getMasterkeyPath() != null) {
+                        metadata.add(vault.getMasterkeyPath());
                     }
                     if(find.find(vault.getConfig())) {
                         metadata.add(vault.getConfig());
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryUVFFeature.java
similarity index 54%
rename from cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java
rename to cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryUVFFeature.java
index 1f92916e928..1dcacf4cd28 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryUVFFeature.java
@@ -1,7 +1,7 @@
 package ch.cyberduck.core.cryptomator.features;
 
 /*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
  * https://cyberduck.io/
  *
  * This program is free software; you can redistribute it and/or modify
@@ -16,11 +16,9 @@
  */
 
 import ch.cyberduck.core.Path;
-import ch.cyberduck.core.RandomStringService;
 import ch.cyberduck.core.Session;
-import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.ContentWriter;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Directory;
@@ -30,64 +28,67 @@
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
 import org.cryptomator.cryptolib.api.FileHeader;
 
-import java.nio.charset.StandardCharsets;
+import java.util.EnumSet;
 
-public class CryptoDirectoryV6Feature implements Directory {
-    private static final Logger log = LogManager.getLogger(CryptoDirectoryV6Feature.class);
+public class CryptoDirectoryUVFFeature extends CryptoDirectoryV7Feature {
+    private static final Logger log = LogManager.getLogger(CryptoDirectoryUVFFeature.class);
 
     private final Session> session;
     private final Directory delegate;
-    private final CryptoVault vault;
-    private final RandomStringService random = new UUIDRandomStringService();
+    private final AbstractVault vault;
 
-    public CryptoDirectoryV6Feature(final Session> session, final Directory delegate, final CryptoVault cryptomator) {
+    public CryptoDirectoryUVFFeature(final Session> session, final Directory delegate, final AbstractVault vault) {
+        super(session, delegate, vault);
         this.session = session;
         this.delegate = delegate;
-        this.vault = cryptomator;
+        this.vault = vault;
     }
 
+    //TODO check if we can merge this implementation with the one in the superclass. Here we additionally write the recovery metadata file.
     @Override
     public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException {
-        final Path encrypt = vault.encrypt(session, folder, random.random(), false);
-        final String directoryId = encrypt.attributes().getDirectoryId();
+        final DirectoryMetadata dirMetadata = vault.getDirectoryProvider().createDirectoryId(folder);
         // Create metadata file for directory
-        final Path directoryMetadataFile = vault.encrypt(session, folder, true);
+        final Path directoryMetadataFolder = session._getFeature(Directory.class).mkdir(writer, vault.encrypt(session, folder, true),
+                new TransferStatus().setRegion(status.getRegion()));
+        final Path directoryMetadataFile = new Path(directoryMetadataFolder,
+                vault.getDirectoryMetadataFilename(),
+                EnumSet.of(Path.Type.file));
         log.debug("Write metadata {} for folder {}", directoryMetadataFile, folder);
-        new ContentWriter(session).write(directoryMetadataFile, directoryId.getBytes(StandardCharsets.UTF_8));
+        final byte[] encryptedMetadata = this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(dirMetadata);
+        new ContentWriter(session).write(directoryMetadataFile, encryptedMetadata);
+        final Path encrypt = vault.encrypt(session, folder, false);
         final Path intermediate = encrypt.getParent();
         if(!session._getFeature(Find.class).find(intermediate)) {
-            session._getFeature(Directory.class).mkdir(session._getFeature(Write.class), intermediate, new TransferStatus().setRegion(status.getRegion()));
+            session._getFeature(Directory.class).mkdir(writer, intermediate, new TransferStatus().setRegion(status.getRegion()));
         }
-        // Write header
+
+        // Write metadata
         final FileHeader header = vault.getFileHeaderCryptor().create();
         status.setHeader(vault.getFileHeaderCryptor().encryptHeader(header));
         status.setNonces(new RandomNonceGenerator(vault.getNonceSize()));
         final Path target = delegate.mkdir(writer, encrypt, status);
+        final Path recoveryDirectoryMetadataFile = new Path(target,
+                vault.getDirectoryMetadataFilename(),
+                EnumSet.of(Path.Type.file));
+        log.debug("Write recovery metadata {} for folder {}", recoveryDirectoryMetadataFile, folder);
+        new ContentWriter(session).write(recoveryDirectoryMetadataFile, this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(dirMetadata));
         // Implementation may return new copy of attributes without encryption attributes
-        target.attributes().setDirectoryId(directoryId);
+        target.attributes().setDirectoryId(encryptedMetadata);
         target.attributes().setDecrypted(folder);
         // Make reference of encrypted path in attributes of decrypted file point to metadata file
         final Path decrypt = vault.decrypt(session, vault.encrypt(session, target, true));
-        decrypt.attributes().setFileId(directoryMetadataFile.attributes().getFileId());
-        decrypt.attributes().setVersionId(directoryMetadataFile.attributes().getVersionId());
+        decrypt.attributes().setFileId(directoryMetadataFolder.attributes().getFileId());
+        decrypt.attributes().setVersionId(directoryMetadataFolder.attributes().getVersionId());
         return decrypt;
     }
 
-    @Override
-    public boolean isSupported(final Path workdir, final String name) {
-        return delegate.isSupported(workdir, name);
-    }
-
-    @Override
-    public void preflight(final Path workdir, final String filename) throws BackgroundException {
-        delegate.preflight(vault.encrypt(session, workdir), filename);
-    }
-
     @Override
     public String toString() {
-        final StringBuilder sb = new StringBuilder("CryptoDirectoryFeature{");
+        final StringBuilder sb = new StringBuilder("CryptoDirectoryUVFFeature{");
         sb.append("proxy=").append(delegate);
         sb.append('}');
         return sb.toString();
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java
index b32f8eb7733..20ed3259674 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java
@@ -16,12 +16,9 @@
  */
 
 import ch.cyberduck.core.Path;
-import ch.cyberduck.core.RandomStringService;
 import ch.cyberduck.core.Session;
-import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.ContentWriter;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider;
 import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Directory;
@@ -31,9 +28,9 @@
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
 import org.cryptomator.cryptolib.api.FileHeader;
 
-import java.nio.charset.StandardCharsets;
 import java.util.EnumSet;
 
 public class CryptoDirectoryV7Feature implements Directory {
@@ -41,28 +38,28 @@ public class CryptoDirectoryV7Feature implements Directory {
 
     private final Session> session;
     private final Directory delegate;
-    private final CryptoVault vault;
-    private final RandomStringService random = new UUIDRandomStringService();
+    private final AbstractVault vault;
 
-    public CryptoDirectoryV7Feature(final Session> session, final Directory delegate, final CryptoVault cryptomator) {
+    public CryptoDirectoryV7Feature(final Session> session, final Directory delegate, final AbstractVault vault) {
         this.session = session;
         this.delegate = delegate;
-        this.vault = cryptomator;
+        this.vault = vault;
     }
 
     @Override
     public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException {
-        final Path encrypt = vault.encrypt(session, folder, random.random(), false);
-        final String directoryId = encrypt.attributes().getDirectoryId();
+        final DirectoryMetadata dirMetadata = vault.getDirectoryProvider().createDirectoryId(folder);
         // Create metadata file for directory
         final Path directoryMetadataFolder = session._getFeature(Directory.class).mkdir(
                 session._getFeature(Write.class), vault.encrypt(session, folder, true),
                 new TransferStatus().setRegion(status.getRegion()));
         final Path directoryMetadataFile = new Path(directoryMetadataFolder,
-                CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE,
+                vault.getDirectoryMetadataFilename(),
                 EnumSet.of(Path.Type.file));
         log.debug("Write metadata {} for folder {}", directoryMetadataFile, folder);
-        new ContentWriter(session).write(directoryMetadataFile, directoryId.getBytes(StandardCharsets.UTF_8));
+        final byte[] encryptedMetadata = this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(dirMetadata);
+        new ContentWriter(session).write(directoryMetadataFile, encryptedMetadata);
+        final Path encrypt = vault.encrypt(session, folder, false);
         final Path intermediate = encrypt.getParent();
         if(!session._getFeature(Find.class).find(intermediate)) {
             session._getFeature(Directory.class).mkdir(session._getFeature(Write.class), intermediate, new TransferStatus().setRegion(status.getRegion()));
@@ -73,7 +70,7 @@ public Path mkdir(final Write writer, final Path folder, final TransferSt
         status.setNonces(new RandomNonceGenerator(vault.getNonceSize()));
         final Path target = delegate.mkdir(writer, encrypt, status);
         // Implementation may return new copy of attributes without encryption attributes
-        target.attributes().setDirectoryId(directoryId);
+        target.attributes().setDirectoryId(encryptedMetadata);
         target.attributes().setDecrypted(folder);
         // Make reference of encrypted path in attributes of decrypted file point to metadata file
         final Path decrypt = vault.decrypt(session, vault.encrypt(session, target, true));
@@ -94,7 +91,7 @@ public void preflight(final Path workdir, final String filename) throws Backgrou
 
     @Override
     public String toString() {
-        final StringBuilder sb = new StringBuilder("CryptoDirectoryFeature{");
+        final StringBuilder sb = new StringBuilder("CryptoDirectoryV7Feature{");
         sb.append("proxy=").append(delegate);
         sb.append('}');
         return sb.toString();
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java
index e3dceec4da6..17119b99785 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java
@@ -19,12 +19,11 @@
 import ch.cyberduck.core.Local;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.exception.NotfoundException;
 import ch.cyberduck.core.features.Download;
 import ch.cyberduck.core.features.Read;
-import ch.cyberduck.core.features.Vault;
 import ch.cyberduck.core.io.BandwidthThrottle;
 import ch.cyberduck.core.io.StreamListener;
 import ch.cyberduck.core.transfer.TransferStatus;
@@ -33,9 +32,9 @@ public class CryptoDownloadFeature implements Download {
 
     private final Session> session;
     private final Download proxy;
-    private final Vault vault;
+    private final AbstractVault vault;
 
-    public CryptoDownloadFeature(final Session> session, final Download proxy, final CryptoVault vault) {
+    public CryptoDownloadFeature(final Session> session, final Download proxy, final Read reader, final AbstractVault vault) {
         this.session = session;
         this.proxy = proxy;
         this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoEncryptionFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoEncryptionFeature.java
index 9f30a97b1be..6dc07f6edf2 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoEncryptionFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoEncryptionFeature.java
@@ -18,7 +18,6 @@
 import ch.cyberduck.core.LoginCallback;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Encryption;
 import ch.cyberduck.core.features.Vault;
@@ -31,7 +30,7 @@ public class CryptoEncryptionFeature implements Encryption {
     private final Encryption delegate;
     private final Vault vault;
 
-    public CryptoEncryptionFeature(final Session> session, final Encryption delegate, final CryptoVault vault) {
+    public CryptoEncryptionFeature(final Session> session, final Encryption delegate, final Vault vault) {
         this.session = session;
         this.delegate = delegate;
         this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java
deleted file mode 100644
index e43afc0965d..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package ch.cyberduck.core.cryptomator.features;
-
-/*
- * Copyright (c) 2002-2017 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.ConnectionCallback;
-import ch.cyberduck.core.LocaleFactory;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.InvalidFilenameException;
-import ch.cyberduck.core.features.Delete;
-import ch.cyberduck.core.features.Move;
-import ch.cyberduck.core.transfer.TransferStatus;
-
-import java.text.MessageFormat;
-import java.util.EnumSet;
-import java.util.Optional;
-
-public class CryptoMoveV6Feature implements Move {
-
-    private final Session> session;
-    private final Move proxy;
-    private final CryptoVault vault;
-
-    public CryptoMoveV6Feature(final Session> session, final Move delegate, final CryptoVault cryptomator) {
-        this.session = session;
-        this.proxy = delegate;
-        this.vault = cryptomator;
-    }
-
-    @Override
-    public Path move(final Path file, final Path renamed, final TransferStatus status, final Delete.Callback callback, final ConnectionCallback connectionCallback) throws BackgroundException {
-        // Move inside vault moves actual files and only metadata files for directories but not the actual directories
-        final Path target = proxy.move(
-                vault.encrypt(session, file, file.isDirectory()),
-                vault.encrypt(session, renamed, file.isDirectory()), status, callback, connectionCallback);
-        if(file.isDirectory()) {
-            vault.getDirectoryProvider().delete(file);
-        }
-        if(vault.contains(target)) {
-            return vault.decrypt(session, target);
-        }
-        return target;
-    }
-
-    @Override
-    public EnumSet features(final Path source, final Path target) {
-        // No need to handle recursion with encrypted filenames
-        return EnumSet.of(Flags.recursive);
-    }
-
-    @Override
-    public void preflight(final Path source, final Optional target) throws BackgroundException {
-        if(target.isPresent()) {
-            if(!vault.getFilenameProvider().isValid(target.get().getName())) {
-                throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.get().getName())).withFile(source);
-            }
-            proxy.preflight(vault.encrypt(session, source, source.isDirectory()), Optional.of(vault.encrypt(session, target.get(), source.isDirectory())));
-        }
-        else {
-            proxy.preflight(vault.encrypt(session, source, source.isDirectory()), target);
-        }
-    }
-
-    @Override
-    public Move withTarget(final Session> session) {
-        proxy.withTarget(session);
-        return this;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder("CryptoMoveFeature{");
-        sb.append("proxy=").append(proxy);
-        sb.append('}');
-        return sb.toString();
-    }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV7Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV7Feature.java
index a0bd1c8eb7f..ea1c2fd5659 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV7Feature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV7Feature.java
@@ -19,8 +19,7 @@
 import ch.cyberduck.core.LocaleFactory;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.exception.InvalidFilenameException;
 import ch.cyberduck.core.features.Delete;
@@ -35,12 +34,12 @@ public class CryptoMoveV7Feature implements Move {
 
     private final Session> session;
     private final Move proxy;
-    private final CryptoVault vault;
+    private final AbstractVault vault;
 
-    public CryptoMoveV7Feature(final Session> session, final Move delegate, final CryptoVault cryptomator) {
+    public CryptoMoveV7Feature(final Session> session, final Move delegate, final AbstractVault vault) {
         this.session = session;
         this.proxy = delegate;
-        this.vault = cryptomator;
+        this.vault = vault;
     }
 
     @Override
@@ -51,8 +50,8 @@ public Path move(final Path file, final Path renamed, final TransferStatus statu
         final Path target = proxy.move(sourceEncrypted, targetEncrypted, status, callback, connectionCallback);
         if(file.isDirectory()) {
             if(!proxy.isRecursive(file, renamed)) {
-                proxy.move(new Path(sourceEncrypted, CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, EnumSet.of(Path.Type.file)),
-                        new Path(targetEncrypted, CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, EnumSet.of(Path.Type.file)),
+                proxy.move(new Path(sourceEncrypted, vault.getDirectoryMetadataFilename(), EnumSet.of(Path.Type.file)),
+                        new Path(targetEncrypted, vault.getBackupDirectoryMetadataFilename(), EnumSet.of(Path.Type.file)),
                         new TransferStatus(status), callback, connectionCallback);
             }
             vault.getDirectoryProvider().delete(file);
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java
index 4943f838a28..f5d6de84717 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java
@@ -16,18 +16,18 @@
  */
 
 import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.features.AttributesFinder;
 import ch.cyberduck.core.features.Find;
 import ch.cyberduck.core.features.MultipartWrite;
 import ch.cyberduck.core.features.Write;
 
 public class CryptoMultipartWriteFeature extends CryptoWriteFeature implements MultipartWrite {
-    public CryptoMultipartWriteFeature(final Session> session, final Write delegate, final CryptoVault vault) {
+    public CryptoMultipartWriteFeature(final Session> session, final Write delegate, final AbstractVault vault) {
         super(session, delegate, vault);
     }
 
-    public CryptoMultipartWriteFeature(final Session> session, final Write delegate, final Find finder, final AttributesFinder attributes, final CryptoVault vault) {
+    public CryptoMultipartWriteFeature(final Session> session, final Write delegate, final Find finder, final AttributesFinder attributes, final AbstractVault vault) {
         super(session, delegate, vault);
     }
 }
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java
index ec8286a4624..45e499a7ed2 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java
@@ -19,8 +19,8 @@
 import ch.cyberduck.core.DefaultIOExceptionMappingService;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.CryptoInputStream;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Read;
 import ch.cyberduck.core.transfer.TransferStatus;
@@ -36,9 +36,9 @@ public class CryptoReadFeature implements Read {
 
     private final Session> session;
     private final Read proxy;
-    private final CryptoVault vault;
+    private final AbstractVault vault;
 
-    public CryptoReadFeature(final Session> session, final Read proxy, final CryptoVault vault) {
+    public CryptoReadFeature(final Session> session, final Read proxy, final AbstractVault vault) {
         this.session = session;
         this.proxy = proxy;
         this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java
index 9db63f0ae3d..437a3c0697e 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java
@@ -17,8 +17,8 @@
 
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.CryptoTransferStatus;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Timestamp;
 import ch.cyberduck.core.transfer.TransferStatus;
@@ -27,9 +27,9 @@ public class CryptoTimestampFeature implements Timestamp {
 
     private final Session> session;
     private final Timestamp proxy;
-    private final CryptoVault vault;
+    private final AbstractVault vault;
 
-    public CryptoTimestampFeature(final Session> session, final Timestamp proxy, final CryptoVault vault) {
+    public CryptoTimestampFeature(final Session> session, final Timestamp proxy, final AbstractVault vault) {
         this.session = session;
         this.proxy = proxy;
         this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java
index e8864566a94..7a0493e49d3 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java
@@ -19,7 +19,7 @@
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.PathAttributes;
 import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.exception.InvalidFilenameException;
@@ -35,9 +35,9 @@ public class CryptoTouchFeature implements Touch {
 
     private final Session> session;
     private final Touch proxy;
-    private final CryptoVault vault;
+    private final AbstractVault vault;
 
-    public CryptoTouchFeature(final Session> session, final Touch proxy, final CryptoVault cryptomator) {
+    public CryptoTouchFeature(final Session> session, final Touch proxy, final AbstractVault cryptomator) {
         this.session = session;
         this.proxy = proxy;
         this.vault = cryptomator;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java
index 2f2c1317338..4d65ed8745d 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java
@@ -20,8 +20,8 @@
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.ProgressListener;
 import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.CryptoTransferStatus;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.features.Upload;
 import ch.cyberduck.core.features.Write;
@@ -33,9 +33,9 @@ public class CryptoUploadFeature implements Upload {
 
     private final Session> session;
     private final Upload proxy;
-    private final CryptoVault vault;
+    private final AbstractVault vault;
 
-    public CryptoUploadFeature(final Session> session, final Upload delegate, final CryptoVault vault) {
+    public CryptoUploadFeature(final Session> session, final Upload delegate, final AbstractVault vault) {
         this.session = session;
         this.proxy = delegate;
         this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoWriteFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoWriteFeature.java
index 7a8ea6f211b..7142146e0e1 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoWriteFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoWriteFeature.java
@@ -19,9 +19,9 @@
 import ch.cyberduck.core.DefaultIOExceptionMappingService;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
 import ch.cyberduck.core.cryptomator.CryptoOutputStream;
 import ch.cyberduck.core.cryptomator.CryptoTransferStatus;
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
 import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator;
 import ch.cyberduck.core.exception.BackgroundException;
@@ -42,9 +42,9 @@ public class CryptoWriteFeature implements Write {
 
     private final Session> session;
     private final Write proxy;
-    private final CryptoVault vault;
+    private final AbstractVault vault;
 
-    public CryptoWriteFeature(final Session> session, final Write proxy, final CryptoVault vault) {
+    public CryptoWriteFeature(final Session> session, final Write proxy, final AbstractVault vault) {
         this.session = session;
         this.proxy = proxy;
         this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryUVFProvider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryUVFProvider.java
new file mode 100644
index 00000000000..4ea8fa352ee
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryUVFProvider.java
@@ -0,0 +1,77 @@
+package ch.cyberduck.core.cryptomator.impl;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.ContentReader;
+import ch.cyberduck.core.cryptomator.CryptoFilename;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.exception.NotfoundException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+
+import java.util.EnumSet;
+
+public class CryptoDirectoryUVFProvider extends CryptoDirectoryV8Provider {
+    private static final Logger log = LogManager.getLogger(CryptoDirectoryUVFProvider.class);
+
+    private final Path home;
+    private final AbstractVault vault;
+    private final CryptoFilename filenameProvider;
+
+    public CryptoDirectoryUVFProvider(final AbstractVault vault, final CryptoFilename filenameProvider) {
+        super(vault, filenameProvider);
+        this.filenameProvider = filenameProvider;
+        this.home = vault.getHome();
+        this.vault = vault;
+    }
+
+    //TODO kann das auch ersetzt werden mit der impl der superklasse? hier wird load verwendet? nötig?
+    @Override
+    public String toEncrypted(final Session> session, final Path parent, final String filename, final EnumSet type) throws BackgroundException {
+        final DirectoryMetadata dirMetadata = load(session, parent);
+        this.vault.getCryptor().directoryContentCryptor().fileNameEncryptor(dirMetadata).encrypt(filename);
+        final String ciphertextName = this.vault.getCryptor().directoryContentCryptor().fileNameEncryptor(dirMetadata).encrypt(filename) + vault.getRegularFileExtension();
+        log.debug("Encrypted filename {} to {}", filename, ciphertextName);
+        return filenameProvider.deflate(session, ciphertextName);
+    }
+
+    protected DirectoryMetadata load(final Session> session, final Path directory) throws BackgroundException {
+        if(new SimplePathPredicate(home).test(directory)) {
+            return vault.getRootDirId();
+        }
+        final Path parent = this.toEncrypted(session, directory.getParent());
+        final String cleartextName = directory.getName();
+        final String ciphertextName = this.toEncrypted(session, directory.getParent(), cleartextName, EnumSet.of(Path.Type.directory));
+        final Path metadataParent = new Path(parent, ciphertextName, EnumSet.of(Path.Type.directory));
+        // Read directory id from file
+        try {
+            log.debug("Read directory ID for folder {} from {}", directory, ciphertextName);
+            final Path metadataFile = new Path(metadataParent, vault.getDirectoryMetadataFilename(), EnumSet.of(Path.Type.file, Path.Type.encrypted));
+            final byte[] ciphertext = new ContentReader(session).readBytes(metadataFile);
+            return this.vault.getCryptor().directoryContentCryptor().decryptDirectoryMetadata(ciphertext);
+        }
+        catch(NotfoundException e) {
+            log.warn("Missing directory ID for folder {}", directory);
+            return this.getOrCreateDirectoryId(session, directory);
+        }
+    }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java
deleted file mode 100644
index 13b1323fb98..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package ch.cyberduck.core.cryptomator.impl;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.CacheReference;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.PathAttributes;
-import ch.cyberduck.core.RandomStringService;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.SimplePathPredicate;
-import ch.cyberduck.core.UUIDRandomStringService;
-import ch.cyberduck.core.cache.LRUCache;
-import ch.cyberduck.core.cryptomator.ContentReader;
-import ch.cyberduck.core.cryptomator.CryptoDirectory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.cryptomator.CryptorCache;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.NotfoundException;
-import ch.cyberduck.core.preferences.PreferencesFactory;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.nio.charset.StandardCharsets;
-import java.util.EnumSet;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-public class CryptoDirectoryV6Provider implements CryptoDirectory {
-    private static final Logger log = LogManager.getLogger(CryptoDirectoryV6Provider.class);
-
-    private static final String DATA_DIR_NAME = "d";
-    private static final String ROOT_DIR_ID = StringUtils.EMPTY;
-
-    private final Path dataRoot;
-    private final Path home;
-    private final CryptoVault cryptomator;
-
-    private final RandomStringService random
-            = new UUIDRandomStringService();
-
-    private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
-    private final LRUCache, String> cache = LRUCache.build(
-            PreferencesFactory.get().getInteger("cryptomator.cache.size"));
-
-    public CryptoDirectoryV6Provider(final Path vault, final CryptoVault cryptomator) {
-        this.home = vault;
-        this.dataRoot = new Path(vault, DATA_DIR_NAME, vault.getType());
-        this.cryptomator = cryptomator;
-    }
-
-    @Override
-    public String toEncrypted(final Session> session, final String directoryId, final String filename, final EnumSet type) throws BackgroundException {
-        final String prefix = type.contains(Path.Type.directory) ? CryptoVault.DIR_PREFIX : "";
-        final String ciphertextName = prefix + cryptomator.getFileNameCryptor().encryptFilename(CryptorCache.BASE32, filename, directoryId.getBytes(StandardCharsets.UTF_8));
-        log.debug("Encrypted filename {} to {}", filename, ciphertextName);
-        return cryptomator.getFilenameProvider().deflate(session, ciphertextName);
-    }
-
-    @Override
-    public Path toEncrypted(final Session> session, final String directoryId, final Path directory) throws BackgroundException {
-        if(!directory.isDirectory()) {
-            throw new NotfoundException(directory.getAbsolute());
-        }
-        if(new SimplePathPredicate(directory).test(home) || directory.isChild(home)) {
-            final PathAttributes attributes = new PathAttributes(directory.attributes());
-            // The root of the vault is a different target directory and file ids always correspond to the metadata file
-            attributes.setVersionId(null);
-            attributes.setFileId(null);
-            // Remember random directory id for use in vault
-            final String id = this.toDirectoryId(session, directory, directoryId);
-            log.debug("Use directory ID '{}' for folder {}", id, directory);
-            attributes.setDirectoryId(id);
-            attributes.setDecrypted(directory);
-            final String directoryIdHash = cryptomator.getFileNameCryptor().hashDirectoryId(id);
-            // Intermediate directory
-            final Path intermediate = new Path(dataRoot, directoryIdHash.substring(0, 2), dataRoot.getType());
-            // Add encrypted type
-            final EnumSet type = EnumSet.copyOf(directory.getType());
-            type.add(Path.Type.encrypted);
-            type.remove(Path.Type.decrypted);
-            return new Path(intermediate, directoryIdHash.substring(2), type, attributes);
-        }
-        throw new NotfoundException(directory.getAbsolute());
-    }
-
-    private String toDirectoryId(final Session> session, final Path directory, final String directoryId) throws BackgroundException {
-        if(new SimplePathPredicate(home).test(directory)) {
-            return ROOT_DIR_ID;
-        }
-        try {
-            lock.readLock().lock();
-            if(cache.contains(new SimplePathPredicate(directory))) {
-                final String existing = cache.get(new SimplePathPredicate(directory));
-                if(StringUtils.isNotBlank(directoryId)) {
-                    if(!existing.equals(directoryId)) {
-                        log.warn("Do not override already cached id {} with {}", existing, directoryId);
-                    }
-                }
-                return existing;
-            }
-        }
-        finally {
-            lock.readLock().unlock();
-        }
-        try {
-            log.debug("Acquire lock for {}", directory);
-            lock.writeLock().lock();
-            final String id = StringUtils.isBlank(directoryId) ? this.load(session, directory) : directoryId;
-            cache.put(new SimplePathPredicate(directory), id);
-            return id;
-        }
-        finally {
-            lock.writeLock().unlock();
-        }
-    }
-
-    protected String load(final Session> session, final Path directory) throws BackgroundException {
-        final Path parent = this.toEncrypted(session, directory.getParent().attributes().getDirectoryId(), directory.getParent());
-        final String cleartextName = directory.getName();
-        final String ciphertextName = this.toEncrypted(session, parent.attributes().getDirectoryId(), cleartextName, EnumSet.of(Path.Type.directory));
-        // Read directory id from file
-        try {
-            log.debug("Read directory ID for folder {} from {}", directory, ciphertextName);
-            final Path metadataFile = new Path(parent, ciphertextName, EnumSet.of(Path.Type.file, Path.Type.encrypted));
-            return new ContentReader(session).read(metadataFile);
-        }
-        catch(NotfoundException e) {
-            log.warn("Missing directory ID for folder {}", directory);
-            return random.random();
-        }
-    }
-
-    public void delete(final Path directory) {
-        try {
-            lock.writeLock().lock();
-            cache.remove(new SimplePathPredicate(directory));
-        }
-        finally {
-            lock.writeLock().unlock();
-        }
-    }
-
-    @Override
-    public void destroy() {
-        try {
-            lock.writeLock().lock();
-            cache.clear();
-        }
-        finally {
-            lock.writeLock().unlock();
-        }
-    }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java
deleted file mode 100644
index 28b4bb6211d..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package ch.cyberduck.core.cryptomator.impl;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.RandomStringService;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.UUIDRandomStringService;
-import ch.cyberduck.core.cryptomator.ContentReader;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.NotfoundException;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.nio.charset.StandardCharsets;
-import java.util.EnumSet;
-
-import com.google.common.io.BaseEncoding;
-
-public class CryptoDirectoryV7Provider extends CryptoDirectoryV6Provider {
-    private static final Logger log = LogManager.getLogger(CryptoDirectoryV7Provider.class);
-
-    public static final String EXTENSION_REGULAR = ".c9r";
-    public static final String FILENAME_DIRECTORYID = "dir";
-    public static final String DIRECTORY_METADATAFILE = String.format("%s%s", FILENAME_DIRECTORYID, EXTENSION_REGULAR);
-    public static final String BACKUP_FILENAME_DIRECTORYID = "dirid";
-    public static final String BACKUP_DIRECTORY_METADATAFILE = String.format("%s%s", BACKUP_FILENAME_DIRECTORYID, EXTENSION_REGULAR);
-
-    private final CryptoVault cryptomator;
-
-    private final RandomStringService random
-        = new UUIDRandomStringService();
-
-    public CryptoDirectoryV7Provider(final Path vault, final CryptoVault cryptomator) {
-        super(vault, cryptomator);
-        this.cryptomator = cryptomator;
-    }
-
-    @Override
-    public String toEncrypted(final Session> session, final String directoryId, final String filename, final EnumSet type) throws BackgroundException {
-        final String ciphertextName = cryptomator.getFileNameCryptor().encryptFilename(BaseEncoding.base64Url(),
-            filename, directoryId.getBytes(StandardCharsets.UTF_8)) + EXTENSION_REGULAR;
-        log.debug("Encrypted filename {} to {}", filename, ciphertextName);
-        return cryptomator.getFilenameProvider().deflate(session, ciphertextName);
-    }
-
-    protected String load(final Session> session, final Path directory) throws BackgroundException {
-        final Path parent = this.toEncrypted(session, directory.getParent().attributes().getDirectoryId(), directory.getParent());
-        final String cleartextName = directory.getName();
-        final String ciphertextName = this.toEncrypted(session, parent.attributes().getDirectoryId(), cleartextName, EnumSet.of(Path.Type.directory));
-        final Path metadataParent = new Path(parent, ciphertextName, EnumSet.of(Path.Type.directory));
-        // Read directory id from file
-        try {
-            log.debug("Read directory ID for folder {} from {}", directory, ciphertextName);
-            final Path metadataFile = new Path(metadataParent, CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, EnumSet.of(Path.Type.file, Path.Type.encrypted));
-            return new ContentReader(session).read(metadataFile);
-        }
-        catch(NotfoundException e) {
-            log.warn("Missing directory ID for folder {}", directory);
-            return random.random();
-        }
-    }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8Provider.java
new file mode 100644
index 00000000000..04faac5007b
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8Provider.java
@@ -0,0 +1,195 @@
+package ch.cyberduck.core.cryptomator.impl;
+
+/*
+ * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.AbstractPath;
+import ch.cyberduck.core.CacheReference;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.PathAttributes;
+import ch.cyberduck.core.RandomStringService;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cache.LRUCache;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.ContentReader;
+import ch.cyberduck.core.cryptomator.CryptoDirectory;
+import ch.cyberduck.core.cryptomator.CryptoFilename;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.exception.NotfoundException;
+import ch.cyberduck.core.preferences.PreferencesFactory;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+
+import java.util.EnumSet;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class CryptoDirectoryV8Provider implements CryptoDirectory {
+    private static final Logger log = LogManager.getLogger(CryptoDirectoryV8Provider.class);
+
+    private static final String DATA_DIR_NAME = "d";
+
+    private final AbstractVault vault;
+    private final Path dataRoot;
+    private final Path home;
+    private final CryptoFilename filenameProvider;
+
+    private final RandomStringService random
+            = new UUIDRandomStringService();
+
+    protected final ReadWriteLock lock = new ReentrantReadWriteLock();
+    protected final LRUCache, byte[]> cache = LRUCache.build(
+            PreferencesFactory.get().getInteger("cryptomator.cache.size"));
+
+
+    public CryptoDirectoryV8Provider(final AbstractVault vault, final CryptoFilename filenameProvider) {
+        this.home = vault.getHome();
+        this.dataRoot = new Path(vault.getHome(), DATA_DIR_NAME, vault.getHome().getType());
+        this.filenameProvider = filenameProvider;
+        this.vault = vault;
+    }
+
+    @Override
+    public String toEncrypted(final Session> session, final Path parent, final String filename, final EnumSet type) throws BackgroundException {
+        final DirectoryMetadata dirMetadata = this.getOrCreateDirectoryId(session, parent);
+        this.vault.getCryptor().directoryContentCryptor().fileNameEncryptor(dirMetadata).encrypt(filename);
+        final String ciphertextName = this.vault.getCryptor().directoryContentCryptor().fileNameEncryptor(dirMetadata).encrypt(filename);
+        log.debug("Encrypted filename {} to {}", filename, ciphertextName);
+        return filenameProvider.deflate(session, ciphertextName);
+    }
+
+    @Override
+    public Path toEncrypted(final Session> session, final Path directory) throws BackgroundException {
+        if(!directory.isDirectory()) {
+            throw new NotfoundException(directory.getAbsolute());
+        }
+        if(new SimplePathPredicate(directory).test(home) || directory.isChild(home)) {
+            final PathAttributes attributes = new PathAttributes(directory.attributes());
+            // The root of the vault is a different target directory and file ids always correspond to the metadata file
+            attributes.setVersionId(null);
+            attributes.setFileId(null);
+            // Remember random directory metadata for use in vault
+            final DirectoryMetadata dirMetadata = this.getOrCreateDirectoryId(session, directory);
+            log.debug("Use directory ID '{}' for folder {}", dirMetadata, directory);
+            attributes.setDirectoryId(this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(dirMetadata));
+            attributes.setDecrypted(directory);
+            final String dirPath = this.vault.getCryptor().directoryContentCryptor().dirPath(dirMetadata);
+            final String[] segments = StringUtils.split(dirPath, '/');
+            // Intermediate directory
+            final Path intermediate = new Path(dataRoot, segments[1], dataRoot.getType());
+            // Add encrypted type
+            final EnumSet type = EnumSet.copyOf(directory.getType());
+            type.add(Path.Type.encrypted);
+            type.remove(Path.Type.decrypted);
+            return new Path(intermediate, segments[2], type, attributes);
+        }
+        throw new NotfoundException(directory.getAbsolute());
+    }
+
+    protected DirectoryMetadata toDirectoryId(final Session> session, final Path directory) throws BackgroundException {
+        if(new SimplePathPredicate(home).test(directory)) {
+            return this.vault.getRootDirId();
+        }
+        lock.readLock().lock();
+        try {
+            if(cache.contains(new SimplePathPredicate(directory))) {
+                return this.vault.getCryptor().directoryContentCryptor().decryptDirectoryMetadata(cache.get(new SimplePathPredicate(directory)));
+            }
+        }
+        finally {
+            lock.readLock().unlock();
+        }
+        try {
+            log.debug("Acquire lock for {}", directory);
+            lock.writeLock().lock();
+            final DirectoryMetadata id = this.load(session, directory);
+            cache.put(new SimplePathPredicate(directory), this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(id));
+            return id;
+        }
+        finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    public void delete(final Path directory) {
+        lock.writeLock().lock();
+        try {
+            cache.remove(new SimplePathPredicate(directory));
+        }
+        finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public void destroy() {
+        lock.writeLock().lock();
+        try {
+            cache.clear();
+        }
+        finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public DirectoryMetadata getOrCreateDirectoryId(final Session> session, final Path file) throws BackgroundException {
+        if(file.attributes().getDirectoryId() != null) {
+            return this.vault.getCryptor().directoryContentCryptor().decryptDirectoryMetadata(file.attributes().getDirectoryId());
+        }
+        final Path decrypted = file.getType().contains(AbstractPath.Type.encrypted) ? file.attributes().getDecrypted() : file;
+        return this.toDirectoryId(session, decrypted.getType().contains(AbstractPath.Type.file) ? decrypted.getParent() : decrypted);
+    }
+
+    @Override
+    public DirectoryMetadata createDirectoryId(final Path directory) {
+        lock.writeLock().lock();
+        try {
+            final DirectoryMetadata metadata = vault.getCryptor().directoryContentCryptor().newDirectoryMetadata();
+            final byte[] encrypted = vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(metadata);
+            cache.put(new SimplePathPredicate(directory), encrypted);
+            return metadata;
+        }
+        finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    protected DirectoryMetadata load(final Session> session, final Path directory) throws BackgroundException {
+        final Path encryptedParent = this.toEncrypted(session, directory.getParent());
+        final String cleartextName = directory.getName();
+        final String ciphertextName = this.toEncrypted(session, directory.getParent(), cleartextName, EnumSet.of(Path.Type.directory));
+        final Path metadataParent = new Path(encryptedParent, ciphertextName, EnumSet.of(Path.Type.directory));
+        // Read directory id from file
+        try {
+            log.debug("Read directory ID for folder {} from {}", directory, ciphertextName);
+            final Path metadataFile = new Path(metadataParent, vault.getDirectoryMetadataFilename(), EnumSet.of(Path.Type.file, Path.Type.encrypted));
+            final byte[] bytes = new ContentReader(session).readBytes(metadataFile);
+            return this.vault.getCryptor().directoryContentCryptor().decryptDirectoryMetadata(bytes);
+        }
+        catch(NotfoundException e) {
+            log.warn("Missing directory ID for folder {}", directory);
+            return this.createDirectoryId(directory);
+            //TODO check if we need to propagtae exception instead?
+            // throw e;
+
+        }
+    }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java
deleted file mode 100644
index 6cb8efa9160..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package ch.cyberduck.core.cryptomator.impl;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cache.LRUCache;
-import ch.cyberduck.core.cryptomator.ContentReader;
-import ch.cyberduck.core.cryptomator.ContentWriter;
-import ch.cyberduck.core.cryptomator.CryptoFilename;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.features.Directory;
-import ch.cyberduck.core.features.Find;
-import ch.cyberduck.core.features.Write;
-import ch.cyberduck.core.preferences.PreferencesFactory;
-import ch.cyberduck.core.transfer.TransferStatus;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.cryptomator.cryptolib.common.MessageDigestSupplier;
-
-import java.util.EnumSet;
-
-import com.google.common.io.BaseEncoding;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-public class CryptoFilenameV6Provider implements CryptoFilename {
-    private static final Logger log = LogManager.getLogger(CryptoFilenameV6Provider.class);
-
-    private static final BaseEncoding BASE32 = BaseEncoding.base32();
-    private static final String LONG_NAME_FILE_EXT = ".lng";
-    private static final String METADATA_DIR_NAME = "m";
-
-    public static final int DEFAULT_NAME_SHORTENING_THRESHOLD = 130;
-
-    private final int shorteningThreshold;
-    private final Path metadataRoot;
-
-    private final LRUCache cache = LRUCache.build(
-        PreferencesFactory.get().getLong("cryptomator.cache.size"));
-
-    public CryptoFilenameV6Provider(final Path vault) {
-        this(vault, DEFAULT_NAME_SHORTENING_THRESHOLD);
-    }
-
-    public CryptoFilenameV6Provider(final Path vault, final int shorteningThreshold) {
-        this.metadataRoot = new Path(vault, METADATA_DIR_NAME, vault.getType());
-        this.shorteningThreshold = shorteningThreshold;
-    }
-
-    @Override
-    public boolean isDeflated(final String filename) {
-        return filename.endsWith(LONG_NAME_FILE_EXT);
-    }
-
-    @Override
-    public boolean isValid(final String filename) {
-        return true;
-    }
-
-    @Override
-    public String inflate(final Session> session, final String shortName) throws BackgroundException {
-        return new ContentReader(session).read(this.resolve(shortName));
-    }
-
-    @Override
-    public String deflate(final Session> session, final String filename) throws BackgroundException {
-        if(filename.length() < shorteningThreshold) {
-            return filename;
-        }
-        if(cache.contains(filename)) {
-            return cache.get(filename);
-        }
-        final byte[] longFileNameBytes = filename.getBytes(UTF_8);
-        final byte[] hash = MessageDigestSupplier.SHA1.get().digest(longFileNameBytes);
-        final String shortName = BASE32.encode(hash) + LONG_NAME_FILE_EXT;
-        final Path metadataFile = this.resolve(shortName);
-        final Path secondLevel = metadataFile.getParent();
-        final Path firstLevel = secondLevel.getParent();
-        final Directory mkdir = session._getFeature(Directory.class);
-        final Find find = session._getFeature(Find.class);
-        if(!find.find(metadataRoot)) {
-            mkdir.mkdir(session._getFeature(Write.class), metadataRoot, new TransferStatus());
-        }
-        if(!find.find(firstLevel)) {
-            mkdir.mkdir(session._getFeature(Write.class), firstLevel, new TransferStatus());
-        }
-        if(!find.find(secondLevel)) {
-            mkdir.mkdir(session._getFeature(Write.class), secondLevel, new TransferStatus());
-        }
-        if(!find.find(metadataFile)) {
-            new ContentWriter(session).write(metadataFile, longFileNameBytes);
-        }
-        log.info("Deflated {} to {}", filename, shortName);
-        cache.put(filename, shortName);
-        return shortName;
-    }
-
-    @Override
-    public Path resolve(final String filename) {
-        // Intermediate directory
-        final Path first = new Path(metadataRoot, filename.substring(0, 2), metadataRoot.getType());
-        // Intermediate directory
-        final Path second = new Path(first, filename.substring(2, 4), metadataRoot.getType());
-        return new Path(second, filename, EnumSet.of(Path.Type.file, Path.Type.encrypted, Path.Type.vault));
-    }
-
-    @Override
-    public void invalidate(final String filename) {
-        cache.remove(filename);
-    }
-
-    @Override
-    public void destroy() {
-        cache.clear();
-    }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/CryptoVault.java
new file mode 100644
index 00000000000..a3428fcea57
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/CryptoVault.java
@@ -0,0 +1,291 @@
+package ch.cyberduck.core.cryptomator.impl.uvf;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.LocaleFactory;
+import ch.cyberduck.core.LoginOptions;
+import ch.cyberduck.core.PasswordCallback;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.PathAttributes;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.CryptoDirectory;
+import ch.cyberduck.core.cryptomator.CryptoFilename;
+import ch.cyberduck.core.cryptomator.features.CryptoDirectoryUVFFeature;
+import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryUVFProvider;
+import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider;
+import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.features.Directory;
+import ch.cyberduck.core.features.Vault;
+import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.Cryptor;
+import org.cryptomator.cryptolib.api.CryptorProvider;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+import org.cryptomator.cryptolib.api.FileContentCryptor;
+import org.cryptomator.cryptolib.api.FileHeaderCryptor;
+import org.cryptomator.cryptolib.api.RevolvingMasterkey;
+import org.cryptomator.cryptolib.api.UVFMasterkey;
+
+import java.text.MessageFormat;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(Vault.class)
+public class CryptoVault extends AbstractVault {
+    private static final Logger log = LogManager.getLogger(CryptoVault.class);
+
+    private static final String REGULAR_FILE_EXTENSION = ".uvf";
+    private static final String FILENAME_DIRECTORYID = "dir";
+    private static final String DIRECTORY_METADATA_FILENAME = String.format("%s%s", FILENAME_DIRECTORYID, REGULAR_FILE_EXTENSION);
+    private static final String BACKUP_FILENAME_DIRECTORYID = "dirid";
+    private static final String BACKUP_DIRECTORY_METADATA_FILENAME = String.format("%s%s", BACKUP_FILENAME_DIRECTORYID, REGULAR_FILE_EXTENSION);
+
+    private static final Pattern FILENAME_PATTERN = Pattern.compile("^([A-Za-z0-9_=-]+)" + REGULAR_FILE_EXTENSION);
+
+    /**
+     * Root of vault directory
+     */
+    private final Path home;
+
+    private RevolvingMasterkey masterKey;
+
+    private Cryptor cryptor;
+    private CryptoFilename filenameProvider;
+    private CryptoDirectory directoryProvider;
+
+    private int nonceSize;
+
+    public CryptoVault(final Path home) {
+        this.home = home;
+    }
+
+    @Override
+    public AbstractVault create(final Session> session, final String region, final VaultCredentials credentials) throws BackgroundException {
+        throw new UnsupportedOperationException();
+    }
+
+    // load -> unlock -> open
+    @Override
+    public CryptoVault load(final Session> session, final PasswordCallback prompt) throws BackgroundException {
+        masterKey = UVFMasterkey.fromDecryptedPayload(prompt.prompt(session.getHost(),
+                LocaleFactory.localizedString("Unlock Vault", "Cryptomator"),
+                MessageFormat.format(LocaleFactory.localizedString("Provide your passphrase to unlock the Cryptomator Vault {0}", "Cryptomator"), home.getName()),
+                new LoginOptions()
+                        .save(false)
+                        .user(false)
+                        .anonymous(false)
+                        .icon("cryptomator.tiff")
+                        .passwordPlaceholder(LocaleFactory.localizedString("Passphrase", "Cryptomator"))).getPassword());
+        final CryptorProvider provider = CryptorProvider.forScheme(CryptorProvider.Scheme.UVF_DRAFT);
+        log.debug("Initialized crypto provider {}", provider);
+        this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide());
+        this.filenameProvider = new CryptoFilenameV7Provider(Integer.MAX_VALUE);
+        this.directoryProvider = new CryptoDirectoryUVFProvider(this, filenameProvider);
+        this.nonceSize = 12;
+        return this;
+    }
+
+    @Override
+    public Path encrypt(Session> session, Path file, boolean metadata) throws BackgroundException {
+        final Path encrypted;
+        if(file.isFile() || metadata) {
+            if(file.getType().contains(Path.Type.vault)) {
+                log.warn("Skip file {} because it is marked as an internal vault path", file);
+                return file;
+            }
+            if(new SimplePathPredicate(file).test(this.getHome())) {
+                log.warn("Skip vault home {} because the root has no metadata file", file);
+                return file;
+            }
+            final Path parent;
+            final String filename;
+            if(file.getType().contains(Path.Type.encrypted)) {
+                final Path decrypted = file.attributes().getDecrypted();
+                parent = this.getDirectoryProvider().toEncrypted(session, decrypted.getParent());
+                filename = this.getDirectoryProvider().toEncrypted(session, decrypted.getParent(), decrypted.getName(), decrypted.getType());
+            }
+            else {
+                parent = this.getDirectoryProvider().toEncrypted(session, file.getParent());
+                // / diff to AbstractVault.encrypt
+                filename = this.getDirectoryProvider().toEncrypted(session, file.getParent(), file.getName(), file.getType());
+                // \ diff to AbstractVault.decrypt
+            }
+            final PathAttributes attributes = new PathAttributes(file.attributes());
+            if(!file.isFile() && !metadata) {
+                // The directory is different from the metadata file used to resolve the actual folder
+                attributes.setVersionId(null);
+                attributes.setFileId(null);
+            }
+            // Translate file size
+            attributes.setSize(this.toCiphertextSize(0L, file.attributes().getSize()));
+            final EnumSet type = EnumSet.copyOf(file.getType());
+            type.remove(Path.Type.decrypted);
+            type.add(Path.Type.encrypted);
+            encrypted = new Path(parent, filename, type, attributes);
+        }
+        else {
+            if(file.getType().contains(Path.Type.encrypted)) {
+                log.warn("Skip file {} because it is already marked as an encrypted path", file);
+                return file;
+            }
+            if(file.getType().contains(Path.Type.vault)) {
+                return this.getDirectoryProvider().toEncrypted(session, this.getHome());
+            }
+            encrypted = this.getDirectoryProvider().toEncrypted(session, file);
+        }
+        // Add reference to decrypted file
+        if(!file.getType().contains(Path.Type.encrypted)) {
+            encrypted.attributes().setDecrypted(file);
+        }
+        // Add reference for vault
+        file.attributes().setVaultMetadata(this.getMetadata());
+        encrypted.attributes().setVaultMetadata(this.getMetadata());
+        return encrypted;
+    }
+
+    @Override
+    public synchronized void close() {
+        super.close();
+        cryptor.destroy();
+    }
+
+    @Override
+    public Path getMasterkeyPath() {
+        //TODO: implement
+        return null;
+    }
+
+    @Override
+    public RevolvingMasterkey getMasterkey() {
+        return masterKey;
+    }
+
+    @Override
+    public Path getConfig() {
+        //TODO: implement
+        return null;
+    }
+
+    @Override
+    public Path getHome() {
+        return home;
+    }
+
+    @Override
+    public FileHeaderCryptor getFileHeaderCryptor() {
+        return cryptor.fileHeaderCryptor();
+    }
+
+    @Override
+    public FileContentCryptor getFileContentCryptor() {
+        return cryptor.fileContentCryptor();
+    }
+
+    @Override
+    public CryptoFilename getFilenameProvider() {
+        return filenameProvider;
+    }
+
+    @Override
+    public CryptoDirectory getDirectoryProvider() {
+        return directoryProvider;
+    }
+
+    @Override
+    public Cryptor getCryptor() {
+        return cryptor;
+    }
+
+    @Override
+    public int getNonceSize() {
+        return nonceSize;
+    }
+
+    @Override
+    public Pattern getFilenamePattern() {
+        return FILENAME_PATTERN;
+    }
+
+    @Override
+    public String getRegularFileExtension() {
+        return REGULAR_FILE_EXTENSION;
+    }
+
+    @Override
+    public String getDirectoryMetadataFilename() {
+        return DIRECTORY_METADATA_FILENAME;
+    }
+
+    @Override
+    public String getBackupDirectoryMetadataFilename() {
+        return BACKUP_DIRECTORY_METADATA_FILENAME;
+    }
+
+    public DirectoryMetadata getRootDirId() {
+        return this.cryptor.directoryContentCryptor().rootDirectoryMetadata();
+    }
+
+    @Override
+    public VaultMetadata getMetadata() {
+        return new VaultMetadata(this.getHome(), VaultMetadata.Type.UVF);
+    }
+
+    @Override
+    public  T getFeature(final Session> session, final Class type, final T delegate) {
+
+        if(type == Directory.class) {
+            return (T) new CryptoDirectoryUVFFeature(session, (Directory) delegate, this
+            );
+        }
+
+        return super.getFeature(session, type, delegate);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if(this == o) {
+            return true;
+        }
+        if(!(o instanceof CryptoVault)) {
+            return false;
+        }
+        final CryptoVault that = (CryptoVault) o;
+        return new SimplePathPredicate(home).test(that.home);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(new SimplePathPredicate(home));
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("UVFVault{");
+        sb.append("home=").append(home);
+        sb.append(", cryptor=").append(cryptor);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVault.java
new file mode 100644
index 00000000000..9a9e0c1a2ce
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVault.java
@@ -0,0 +1,514 @@
+package ch.cyberduck.core.cryptomator.impl.v8;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.Credentials;
+import ch.cyberduck.core.DescriptiveUrl;
+import ch.cyberduck.core.Host;
+import ch.cyberduck.core.LocaleFactory;
+import ch.cyberduck.core.LoginOptions;
+import ch.cyberduck.core.PasswordCallback;
+import ch.cyberduck.core.PasswordStore;
+import ch.cyberduck.core.PasswordStoreFactory;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.ContentReader;
+import ch.cyberduck.core.cryptomator.ContentWriter;
+import ch.cyberduck.core.cryptomator.CryptoAuthenticationException;
+import ch.cyberduck.core.cryptomator.CryptoDirectory;
+import ch.cyberduck.core.cryptomator.CryptoFilename;
+import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV8Provider;
+import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider;
+import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.exception.LocalAccessDeniedException;
+import ch.cyberduck.core.exception.LoginCanceledException;
+import ch.cyberduck.core.exception.NotfoundException;
+import ch.cyberduck.core.features.Directory;
+import ch.cyberduck.core.features.Encryption;
+import ch.cyberduck.core.features.Vault;
+import ch.cyberduck.core.features.Write;
+import ch.cyberduck.core.preferences.Preferences;
+import ch.cyberduck.core.preferences.PreferencesFactory;
+import ch.cyberduck.core.shared.DefaultUrlProvider;
+import ch.cyberduck.core.transfer.TransferStatus;
+import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultException;
+import ch.cyberduck.core.vault.VaultMetadata;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.Cryptor;
+import org.cryptomator.cryptolib.api.CryptorProvider;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+import org.cryptomator.cryptolib.api.FileContentCryptor;
+import org.cryptomator.cryptolib.api.FileHeaderCryptor;
+import org.cryptomator.cryptolib.api.InvalidPassphraseException;
+import org.cryptomator.cryptolib.api.Masterkey;
+import org.cryptomator.cryptolib.api.PerpetualMasterkey;
+import org.cryptomator.cryptolib.common.MasterkeyFile;
+import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.InvalidClaimException;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.auth0.jwt.exceptions.SignatureVerificationException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.google.auto.service.AutoService;
+import com.google.gson.JsonParseException;
+
+@AutoService(Vault.class)
+public class CryptoVault extends AbstractVault {
+
+    private static final Logger log = LogManager.getLogger(CryptoVault.class);
+
+
+    public static final String REGULAR_FILE_EXTENSION = ".c9r";
+    public static final int VAULT_VERSION = 8;
+
+    private static final String FILENAME_DIRECTORYID = "dir";
+    private static final String DIRECTORY_METADATA_FILENAME = String.format("%s%s", FILENAME_DIRECTORYID, REGULAR_FILE_EXTENSION);
+    private static final String BACKUP_FILENAME_DIRECTORYID = "dirid";
+    private static final String BACKUP_DIRECTORY_METADATA_FILENAME = String.format("%s%s", BACKUP_FILENAME_DIRECTORYID, REGULAR_FILE_EXTENSION);
+    private static final Pattern FILENAME_PATTERN = Pattern.compile("^([A-Za-z0-9_=-]+)" + REGULAR_FILE_EXTENSION);
+
+    private static final List SUPPORTED_VERSIONS = Arrays.asList(7, 8);
+
+    private static final String JSON_KEY_VAULTVERSION = "format";
+    private static final String JSON_KEY_CIPHERCONFIG = "cipherCombo";
+    private static final String JSON_KEY_SHORTENING_THRESHOLD = "shorteningThreshold";
+
+    private final Path home;
+    private Masterkey masterkey;
+    private final Path masterkeyPath;
+    private final byte[] pepper;
+
+    private final PasswordStore keychain = PasswordStoreFactory.get();
+    private final Preferences preferences = PreferencesFactory.get();
+
+    private final Path config;
+
+    private int nonceSize;
+    private Cryptor cryptor;
+
+    private CryptoFilename filenameProvider;
+    private CryptoDirectory directoryProvider;
+
+    public CryptoVault(final Path home) {
+        this.home = home;
+        this.masterkeyPath = new Path(home, preferences.getProperty("cryptomator.vault.masterkey.filename"), EnumSet.of(Path.Type.file, Path.Type.vault));
+        this.config = new Path(home, preferences.getProperty("cryptomator.vault.config.filename"), EnumSet.of(Path.Type.file, Path.Type.vault));
+        this.pepper = preferences.getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8);
+
+        //TODO nötig?
+        // New vault home with vault flag set for internal use
+        final EnumSet type = EnumSet.copyOf(home.getType());
+        type.add(Path.Type.vault);
+    }
+
+    public Path getHome() {
+        return home;
+    }
+
+    @Override
+    public Path getMasterkeyPath() {
+        return masterkeyPath;
+    }
+
+    public Masterkey getMasterkey() {
+        return masterkey;
+    }
+
+    @Override
+    public Path getConfig() {
+        return config;
+    }
+
+    @Override
+    public FileHeaderCryptor getFileHeaderCryptor() {
+        return cryptor.fileHeaderCryptor();
+    }
+
+    @Override
+    public FileContentCryptor getFileContentCryptor() {
+        return cryptor.fileContentCryptor();
+    }
+
+    @Override
+    public CryptoFilename getFilenameProvider() {
+        return filenameProvider;
+    }
+
+    @Override
+    public CryptoDirectory getDirectoryProvider() {
+        return directoryProvider;
+    }
+
+    @Override
+    public Cryptor getCryptor() {
+        return cryptor;
+    }
+
+    @Override
+    public int getNonceSize() {
+        return nonceSize;
+    }
+
+    @Override
+    public String getRegularFileExtension() {
+        return REGULAR_FILE_EXTENSION;
+    }
+
+    @Override
+    public String getDirectoryMetadataFilename() {
+        return DIRECTORY_METADATA_FILENAME;
+    }
+
+    @Override
+    public String getBackupDirectoryMetadataFilename() {
+        return BACKUP_DIRECTORY_METADATA_FILENAME;
+    }
+
+    @Override
+    public DirectoryMetadata getRootDirId() {
+        return this.cryptor.directoryContentCryptor().rootDirectoryMetadata();
+    }
+
+    @Override
+    public Pattern getFilenamePattern() {
+        return FILENAME_PATTERN;
+    }
+
+    @Override
+    public synchronized AbstractVault create(final Session> session, final String region, final VaultCredentials credentials) throws BackgroundException {
+        final Host bookmark = session.getHost();
+        if(credentials.isSaved()) {
+            try {
+                keychain.addPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
+                        new DefaultUrlProvider(bookmark).toUrl(masterkeyPath, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl(), credentials.getPassword());
+            }
+            catch(LocalAccessDeniedException e) {
+                log.error("Failure {} saving credentials for {} in password store", e, bookmark);
+            }
+        }
+        final String passphrase = credentials.getPassword();
+        final ByteArrayOutputStream mkArray = new ByteArrayOutputStream();
+        final PerpetualMasterkey mk = Masterkey.generate(FastSecureRandomProvider.get().provide());
+        final MasterkeyFileAccess access = new MasterkeyFileAccess(pepper, FastSecureRandomProvider.get().provide());
+        final MasterkeyFile masterkeyFile;
+        try {
+            access.persist(mk, mkArray, passphrase, VAULT_VERSION);
+            masterkeyFile = MasterkeyFile.read(new StringReader(new String(mkArray.toByteArray(), StandardCharsets.UTF_8)));
+        }
+        catch(IOException e) {
+            throw new VaultException("Failure creating master key", e);
+        }
+        log.debug("Write master key to {}", masterkeyPath);
+        // Obtain non encrypted directory writer
+        final Directory> directory = session._getFeature(Directory.class);
+        final TransferStatus status = new TransferStatus().setRegion(region);
+        final Encryption encryption = session._getFeature(Encryption.class);
+        if(encryption != null) {
+            status.setEncryption(encryption.getDefault(home));
+        }
+        final Path vault = directory.mkdir(session._getFeature(Write.class), home, status);
+        new ContentWriter(session).write(masterkeyPath, mkArray.toByteArray());
+        // Create vaultconfig.cryptomator
+        final Algorithm algorithm = Algorithm.HMAC256(mk.getEncoded());
+        final String conf = JWT.create()
+                .withJWTId(new UUIDRandomStringService().random())
+                .withKeyId(String.format("masterkeyfile:%s", masterkeyPath.getName()))
+                .withClaim(JSON_KEY_VAULTVERSION, VAULT_VERSION)
+                .withClaim(JSON_KEY_CIPHERCONFIG, CryptorProvider.Scheme.SIV_GCM.toString())
+                .withClaim(JSON_KEY_SHORTENING_THRESHOLD, CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD)
+                .sign(algorithm);
+        new ContentWriter(session).write(config, conf.getBytes(StandardCharsets.US_ASCII));
+        this.open(parseVaultConfigFromJWT(conf).withMasterkeyFile(masterkeyFile), passphrase);
+        final Path secondLevel = directoryProvider.toEncrypted(session, home);
+        final Path firstLevel = secondLevel.getParent();
+        final Path dataDir = firstLevel.getParent();
+        log.debug("Create vault root directory at {}", secondLevel);
+        directory.mkdir(session._getFeature(Write.class), dataDir, status);
+        directory.mkdir(session._getFeature(Write.class), firstLevel, status);
+        directory.mkdir(session._getFeature(Write.class), secondLevel, status);
+        return this;
+    }
+
+    private static VaultConfig parseVaultConfigFromMasterKey(final MasterkeyFile masterkeyFile) {
+        return new VaultConfig(masterkeyFile.version, CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD,
+                CryptorProvider.Scheme.SIV_CTRMAC, null, null);
+    }
+
+    @Override
+    public Vault load(final Session> session, final PasswordCallback prompt) throws BackgroundException {
+        final Host bookmark = session.getHost();
+        String passphrase = keychain.getPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
+                new DefaultUrlProvider(bookmark).toUrl(masterkeyPath, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl());
+        if(null == passphrase) {
+            // Legacy
+            passphrase = keychain.getPassword(String.format("Cryptomator Passphrase %s", bookmark.getHostname()),
+                    new DefaultUrlProvider(bookmark).toUrl(masterkeyPath, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl());
+        }
+        return this.unlock(session, prompt, bookmark, passphrase);
+    }
+
+    public Vault unlock(final Session> session, final PasswordCallback prompt, final Host bookmark, final String passphrase) throws BackgroundException {
+        final ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig vaultConfig = this.readVaultConfig(session);
+        this.unlock(vaultConfig, passphrase, bookmark, prompt,
+                MessageFormat.format(LocaleFactory.localizedString("Provide your passphrase to unlock the Cryptomator Vault {0}", "Cryptomator"), home.getName())
+        );
+        return this;
+    }
+
+    public void unlock(final ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig vaultConfig, final String passphrase, final Host bookmark, final PasswordCallback prompt,
+                       final String message) throws BackgroundException {
+        final Credentials credentials;
+        if(null == passphrase) {
+            credentials = prompt.prompt(
+                    bookmark, LocaleFactory.localizedString("Unlock Vault", "Cryptomator"),
+                    message,
+                    new LoginOptions()
+                            .save(preferences.getBoolean("cryptomator.vault.keychain"))
+                            .user(false)
+                            .anonymous(false)
+                            .icon("cryptomator.tiff")
+                            .passwordPlaceholder(LocaleFactory.localizedString("Passphrase", "Cryptomator")));
+            if(null == credentials.getPassword()) {
+                throw new LoginCanceledException();
+            }
+        }
+        else {
+            credentials = new VaultCredentials(passphrase).withSaved(false);
+        }
+        try {
+            this.open(vaultConfig, credentials.getPassword());
+            if(credentials.isSaved()) {
+                log.info("Save passphrase for {}", masterkeyPath);
+                // Save password with hostname and path to masterkey.cryptomator in keychain
+                keychain.addPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
+                        new DefaultUrlProvider(bookmark).toUrl(masterkeyPath, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl(), credentials.getPassword());
+            }
+        }
+        catch(CryptoAuthenticationException e) {
+            this.unlock(vaultConfig, null, bookmark, prompt, String.format("%s %s.", e.getDetail(),
+                    MessageFormat.format(LocaleFactory.localizedString("Provide your passphrase to unlock the Cryptomator Vault {0}", "Cryptomator"), home.getName())));
+        }
+    }
+
+    private ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig readVaultConfig(final Session> session) throws BackgroundException {
+        final MasterkeyFile masterkeyFile = this.readMasterkeyFile(session, masterkeyPath);
+        try {
+            return parseVaultConfigFromJWT(new ContentReader(session).read(config)).withMasterkeyFile(masterkeyFile);
+        }
+        catch(NotfoundException e) {
+            log.debug("Ignore failure reading vault configuration {}", config);
+            return parseVaultConfigFromMasterKey(masterkeyFile).withMasterkeyFile(masterkeyFile);
+        }
+    }
+
+    public static ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig parseVaultConfigFromJWT(final String token) {
+        final DecodedJWT decoded = JWT.decode(token);
+        return new ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig(
+                decoded.getClaim(JSON_KEY_VAULTVERSION).asInt(),
+                decoded.getClaim(JSON_KEY_SHORTENING_THRESHOLD).asInt(),
+                CryptorProvider.Scheme.valueOf(decoded.getClaim(JSON_KEY_CIPHERCONFIG).asString()),
+                decoded.getAlgorithm(), decoded);
+    }
+
+    private MasterkeyFile readMasterkeyFile(final Session> session, final Path file) throws BackgroundException {
+        log.debug("Read master key {}", file);
+        try(Reader reader = new ContentReader(session).getReader(file)) {
+            return MasterkeyFile.read(reader);
+        }
+        catch(JsonParseException | IllegalArgumentException | IllegalStateException | IOException e) {
+            throw new VaultException(String.format("Failure reading vault master key file %s", file.getName()), e);
+        }
+    }
+
+    protected void open(final ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig vaultConfig, final CharSequence passphrase) throws BackgroundException {
+        try {
+            final PerpetualMasterkey masterKey = this.getMasterKey(vaultConfig.getMkfile(), passphrase);
+            this.open(vaultConfig, masterKey);
+        }
+        catch(IllegalArgumentException | IOException e) {
+            throw new VaultException("Failure reading key file", e);
+        }
+        catch(InvalidPassphraseException e) {
+            throw new CryptoAuthenticationException("Failure to decrypt master key file", e);
+        }
+    }
+
+    protected void open(final ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig vaultConfig, final PerpetualMasterkey masterKey) throws BackgroundException {
+        if(!SUPPORTED_VERSIONS.contains(vaultConfig.vaultVersion())) {
+            throw new VaultException(String.format("Unsupported vault version %d", vaultConfig.vaultVersion()));
+        }
+        final CryptorProvider provider = CryptorProvider.forScheme(vaultConfig.getCipherCombo());
+        log.debug("Initialized crypto provider {}", provider);
+        vaultConfig.verify(masterKey.getEncoded(), VAULT_VERSION);
+        this.masterkey = masterKey;
+        this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide());
+        this.filenameProvider = new CryptoFilenameV7Provider(vaultConfig.getShorteningThreshold());
+        this.directoryProvider = new CryptoDirectoryV8Provider(this, this.filenameProvider);
+        this.nonceSize = vaultConfig.getNonceSize();
+    }
+
+    private PerpetualMasterkey getMasterKey(final MasterkeyFile mkFile, final CharSequence passphrase) throws IOException {
+        final StringWriter writer = new StringWriter();
+        mkFile.write(writer);
+        return new MasterkeyFileAccess(pepper, FastSecureRandomProvider.get().provide()).load(
+                new ByteArrayInputStream(writer.getBuffer().toString().getBytes(StandardCharsets.UTF_8)), passphrase);
+    }
+
+    @Override
+    public VaultMetadata getMetadata() {
+        return new VaultMetadata(this.getHome(), VaultMetadata.Type.V8);
+    }
+
+    @Override
+    public synchronized void close() {
+        super.close();
+        cryptor = null;
+    }
+
+    public static class VaultConfig {
+
+        private final int version;
+        private final int shorteningThreshold;
+        private final CryptorProvider.Scheme cipherCombo;
+        private final String algorithm;
+        private final DecodedJWT token;
+        private MasterkeyFile mkfile;
+
+        public VaultConfig(int version, int shorteningThreshold, CryptorProvider.Scheme cipherCombo, String algorithm, DecodedJWT token) {
+            this.version = version;
+            this.shorteningThreshold = shorteningThreshold;
+            this.cipherCombo = cipherCombo;
+            this.algorithm = algorithm;
+            this.token = token;
+        }
+
+        public int vaultVersion() {
+            return version;
+        }
+
+        public VaultConfig withMasterkeyFile(final MasterkeyFile mkfile) {
+            this.mkfile = mkfile;
+            return this;
+        }
+
+        public MasterkeyFile getMkfile() {
+            return mkfile;
+        }
+
+        public int getShorteningThreshold() {
+            return shorteningThreshold;
+        }
+
+        public CryptorProvider.Scheme getCipherCombo() {
+            return cipherCombo;
+        }
+
+        public int getNonceSize() throws VaultException {
+            switch(cipherCombo) {
+                case SIV_CTRMAC:
+                    return 16;
+                case SIV_GCM:
+                    return 12;
+                default:
+                    throw new VaultException(String.format("Unsupported cipher scheme %s", cipherCombo));
+            }
+        }
+
+        private Algorithm initAlgorithm(byte[] rawKey) throws VaultException {
+            switch(algorithm) {
+                case "HS256":
+                    return Algorithm.HMAC256(rawKey);
+                case "HS384":
+                    return Algorithm.HMAC384(rawKey);
+                case "HS512":
+                    return Algorithm.HMAC512(rawKey);
+                default:
+                    throw new VaultException(String.format("Unsupported signature algorithm %s", algorithm));
+            }
+        }
+
+        public void verify(byte[] rawKey, int expectedVaultVersion) throws VaultException {
+            try {
+                if(token == null) {
+                    return;
+                }
+                JWTVerifier verifier = JWT.require(initAlgorithm(rawKey))
+                        .withClaim(JSON_KEY_VAULTVERSION, expectedVaultVersion)
+                        .build();
+                verifier.verify(token);
+            }
+            catch(SignatureVerificationException e) {
+                throw new VaultException("Invalid JWT signature", e);
+            }
+            catch(InvalidClaimException e) {
+                throw new VaultException(String.format("Expected vault config for version %d", expectedVaultVersion), e);
+            }
+            catch(JWTVerificationException e) {
+                throw new VaultException(String.format("Failed to verify vault config %s", token), e);
+            }
+        }
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if(this == o) {
+            return true;
+        }
+        if(!(o instanceof CryptoVault)) {
+            return false;
+        }
+        final CryptoVault that = (CryptoVault) o;
+        return new SimplePathPredicate(home).test(that.home);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(new SimplePathPredicate(home));
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("CryptoVault{");
+        sb.append("home=").append(home);
+        sb.append(", cryptor=").append(cryptor);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java
index a539352f90f..4935d094072 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java
@@ -20,6 +20,7 @@
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.TestProtocol;
 import ch.cyberduck.core.cryptomator.features.CryptoChecksumCompute;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
 import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
 import ch.cyberduck.core.features.Directory;
 import ch.cyberduck.core.features.Write;
@@ -61,7 +62,6 @@ public Path mkdir(final Write writer, final Path folder, final TransferStatus st
         cryptomator.create(session, null, new VaultCredentials("test"));
         final ByteBuffer header = cryptomator.getFileHeaderCryptor().encryptHeader(cryptomator.getFileHeaderCryptor().create());
         // DEFAULT_PIPE_SIZE=1024
-        final Path file = new Path(vault, "f", EnumSet.of(Path.Type.file));
         final SHA256ChecksumCompute sha = new SHA256ChecksumCompute();
         final CryptoChecksumCompute compute = new CryptoChecksumCompute(sha, cryptomator);
         final RandomNonceGenerator nonces = new RandomNonceGenerator(cryptomator.getNonceSize());
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java
index eff5c2e279a..a5f848ade45 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java
@@ -19,6 +19,7 @@
 import ch.cyberduck.core.NullSession;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.TestProtocol;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
 import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
 import ch.cyberduck.core.features.Directory;
 import ch.cyberduck.core.features.Write;
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java
index ecacf7650c8..3c2d81679a3 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java
@@ -19,6 +19,7 @@
 import ch.cyberduck.core.NullSession;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.TestProtocol;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
 import ch.cyberduck.core.features.Directory;
 import ch.cyberduck.core.features.Write;
 import ch.cyberduck.core.transfer.TransferStatus;
@@ -33,43 +34,6 @@
 
 public class CryptoWriteFeatureTest {
 
-    @Test
-    public void testCiphertextSize_CTR() throws Exception {
-        final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
-        final NullSession session = new NullSession(new Host(new TestProtocol())) {
-            @Override
-            @SuppressWarnings("unchecked")
-            public  T _getFeature(final Class type) {
-                if(type == Directory.class) {
-                    return (T) new Directory() {
-
-                        @Override
-                        public Path mkdir(final Write writer, final Path folder, final TransferStatus status) {
-                            assertTrue(folder.equals(home) || folder.isChild(home));
-                            return folder;
-                        }
-                    };
-                }
-                return super._getFeature(type);
-            }
-        };
-        final CryptoVault vault = new CryptoVault(home);
-        vault.create(session, null, new VaultCredentials("test"), CryptoVault.VAULT_VERSION_DEPRECATED);
-        int headerSize = vault.getFileHeaderCryptor().headerSize();
-        // zero file size
-        assertEquals(headerSize, vault.toCiphertextSize(0L, 0));
-        // one-byte file
-        assertEquals(headerSize + 48 + 1, vault.toCiphertextSize(0L, 1));
-        // file with chunk size length
-        assertEquals(headerSize + vault.getFileContentCryptor().ciphertextChunkSize(), vault.toCiphertextSize(0L, vault.getFileContentCryptor().cleartextChunkSize()));
-        // file with chunk size length + 1
-        assertEquals(headerSize + vault.getFileContentCryptor().ciphertextChunkSize() + 48 + 1, vault.toCiphertextSize(0L, vault.getFileContentCryptor().cleartextChunkSize() + 1));
-        // file with 2 * chunk size length
-        assertEquals(headerSize + 2 * vault.getFileContentCryptor().ciphertextChunkSize(), vault.toCiphertextSize(0L, 2 * vault.getFileContentCryptor().cleartextChunkSize()));
-        // file with 2 * chunk size length + 100
-        assertEquals(headerSize + 2 * vault.getFileContentCryptor().ciphertextChunkSize() + 48 + 100, vault.toCiphertextSize(0L, 2 * vault.getFileContentCryptor().cleartextChunkSize() + 100));
-    }
-
     @Test
     public void testCiphertextSize_GCM() throws Exception {
         final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
@@ -91,7 +55,7 @@ public Path mkdir(final Write writer, final Path folder, final TransferStatus st
             }
         };
         final CryptoVault vault = new CryptoVault(home);
-        vault.create(session, null, new VaultCredentials("test"), CryptoVault.VAULT_VERSION);
+        vault.create(session, null, new VaultCredentials("test"));
         int headerSize = vault.getFileHeaderCryptor().headerSize();
         // zero file size
         assertEquals(headerSize, vault.toCiphertextSize(0L, 0));
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptorCacheTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptorCacheTest.java
index f88038fc29e..94deb11d3d5 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptorCacheTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptorCacheTest.java
@@ -19,10 +19,11 @@
 import org.cryptomator.cryptolib.api.FileNameCryptor;
 import org.junit.Test;
 
+import java.nio.charset.StandardCharsets;
+
 import com.google.common.io.BaseEncoding;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.*;
 
 public class CryptorCacheTest {
@@ -31,10 +32,10 @@ public class CryptorCacheTest {
     public void TestHashDirectoryId() {
         final FileNameCryptor mock = mock(FileNameCryptor.class);
         final CryptorCache cryptor = new CryptorCache(mock);
-        when(mock.hashDirectoryId(anyString())).thenReturn("hashed");
-        assertEquals("hashed", cryptor.hashDirectoryId("id"));
-        assertEquals("hashed", cryptor.hashDirectoryId("id"));
-        verify(mock, times(1)).hashDirectoryId(anyString());
+        when(mock.hashDirectoryId(any(byte[].class))).thenReturn("hashed");
+        assertEquals("hashed", cryptor.hashDirectoryId("id".getBytes(StandardCharsets.US_ASCII)));
+        assertEquals("hashed", cryptor.hashDirectoryId("id".getBytes(StandardCharsets.US_ASCII)));
+        verify(mock, times(1)).hashDirectoryId(any(byte[].class));
         verifyNoMoreInteractions(mock);
     }
 
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java
index 2bde499b600..bcc398e2bd5 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java
@@ -22,7 +22,7 @@
 import ch.cyberduck.core.NullSession;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.TestProtocol;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
 import ch.cyberduck.core.features.Bulk;
 import ch.cyberduck.core.features.Directory;
 import ch.cyberduck.core.features.Write;
@@ -88,7 +88,7 @@ public boolean test(final TransferItem item) {
                 return item.remote.isDirectory();
             }
         }).findFirst().get().remote;
-        final String directoryId = encryptedDirectory.attributes().getDirectoryId();
+        final byte[] directoryId = encryptedDirectory.attributes().getDirectoryId();
         assertNotNull(directoryId);
         for(TransferItem file : pre.keySet().stream().filter(new Predicate() {
             @Override
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeatureTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeatureTest.java
index b0f53977c41..d87e329fdd8 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeatureTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeatureTest.java
@@ -23,7 +23,7 @@
 import ch.cyberduck.core.NullSession;
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.TestProtocol;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.exception.NotfoundException;
 import ch.cyberduck.core.features.Read;
@@ -38,12 +38,12 @@
 import java.nio.charset.Charset;
 import java.util.EnumSet;
 
-import static ch.cyberduck.core.cryptomator.CryptoVault.VAULT_VERSION;
-import static ch.cyberduck.core.cryptomator.CryptoVaultTest.createJWT;
+import static ch.cyberduck.core.cryptomator.impl.v8.CryptoVaultTest.createJWT;
 import static org.junit.Assert.assertEquals;
 
 public class CryptoReadFeatureTest {
 
+    //TODO prüfen, ob CTR in 7 vorkommen kann. oder nur GCM
     @Test
     public void testCalculations_CTR() throws Exception {
         final NullSession session = new NullSession(new Host(new TestProtocol())) {
@@ -61,7 +61,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
                                     "  \"primaryMasterKey\": \"Q7pGo1l0jmZssoQh9rXFPKJE9NIXvPbL+HcnVSR9CHdkeR8AwgFtcw==\",\n" +
                                     "  \"hmacMasterKey\": \"xzBqT4/7uEcQbhHFLC0YmMy4ykVKbuvJEA46p1Xm25mJNuTc20nCbw==\",\n" +
                                     "  \"versionMac\": \"hlNr3dz/CmuVajhaiGyCem9lcVIUjDfSMLhjppcXOrM=\",\n" +
-                                    "  \"version\": 6\n" +
+                                    "  \"version\": 7\n" +
                                     "}";
                             if("masterkey.cryptomator".equals(file.getName())) {
                                 return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
@@ -141,7 +141,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
                                 return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
                             }
                             if("vault.cryptomator".equals(file.getName())) {
-                                return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault"), Charset.defaultCharset());
+                                return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault"), Charset.defaultCharset());
                             }
                             throw new NotfoundException(String.format("%s not found", file.getName()));
                         }
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6ProviderTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6ProviderTest.java
deleted file mode 100644
index a2f78fa324e..00000000000
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6ProviderTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package ch.cyberduck.core.cryptomator.impl;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.ConnectionCallback;
-import ch.cyberduck.core.Credentials;
-import ch.cyberduck.core.DisabledPasswordCallback;
-import ch.cyberduck.core.Host;
-import ch.cyberduck.core.LoginOptions;
-import ch.cyberduck.core.NullSession;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.TestProtocol;
-import ch.cyberduck.core.cryptomator.CryptoDirectory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.NotfoundException;
-import ch.cyberduck.core.features.Read;
-import ch.cyberduck.core.transfer.TransferStatus;
-import ch.cyberduck.core.vault.VaultCredentials;
-
-import org.apache.commons.io.IOUtils;
-import org.junit.Test;
-
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.util.EnumSet;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-public class CryptoDirectoryV6ProviderTest {
-
-    @Test(expected = NotfoundException.class)
-    public void testToEncryptedInvalidArgument() throws Exception {
-        final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
-        final CryptoVault vault = new CryptoVault(home);
-        final CryptoDirectory provider = new CryptoDirectoryV6Provider(home, vault);
-        provider.toEncrypted(new NullSession(new Host(new TestProtocol())), null, new Path("/vault/f", EnumSet.of(Path.Type.file)));
-    }
-
-    @Test(expected = NotfoundException.class)
-    public void testToEncryptedInvalidPath() throws Exception {
-        final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
-        final CryptoVault vault = new CryptoVault(home);
-        final CryptoDirectory provider = new CryptoDirectoryV6Provider(home, vault);
-        provider.toEncrypted(new NullSession(new Host(new TestProtocol())), null, new Path("/", EnumSet.of(Path.Type.directory)));
-    }
-
-    @Test
-    public void testToEncryptedDirectory() throws Exception {
-        final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
-        final NullSession session = new NullSession(new Host(new TestProtocol())) {
-            @Override
-            @SuppressWarnings("unchecked")
-            public  T _getFeature(final Class type) {
-                if(type == Read.class) {
-                    return (T) new Read() {
-                        @Override
-                        public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
-                            final String masterKey = "{\n" +
-                                    "  \"scryptSalt\": \"NrC7QGG/ouc=\",\n" +
-                                    "  \"scryptCostParam\": 16384,\n" +
-                                    "  \"scryptBlockSize\": 8,\n" +
-                                    "  \"primaryMasterKey\": \"Q7pGo1l0jmZssoQh9rXFPKJE9NIXvPbL+HcnVSR9CHdkeR8AwgFtcw==\",\n" +
-                                    "  \"hmacMasterKey\": \"xzBqT4/7uEcQbhHFLC0YmMy4ykVKbuvJEA46p1Xm25mJNuTc20nCbw==\",\n" +
-                                    "  \"versionMac\": \"hlNr3dz/CmuVajhaiGyCem9lcVIUjDfSMLhjppcXOrM=\",\n" +
-                                    "  \"version\": 5\n" +
-                                    "}";
-                            if("masterkey.cryptomator".equals(file.getName())) {
-                                return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
-                            }
-                            throw new NotfoundException(String.format("%s not found", file.getName()));
-                        }
-
-                        @Override
-                        public boolean offset(final Path file) {
-                            return false;
-                        }
-                    };
-                }
-                return super._getFeature(type);
-            }
-        };
-        final CryptoVault vault = new CryptoVault(home);
-        vault.load(session, new DisabledPasswordCallback() {
-            @Override
-            public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
-                return new VaultCredentials("vault");
-            }
-        });
-        final CryptoDirectory provider = new CryptoDirectoryV6Provider(home, vault);
-        assertNotNull(provider.toEncrypted(session, null, home));
-        final Path f = new Path("/vault/f", EnumSet.of(Path.Type.directory));
-        assertNotNull(provider.toEncrypted(session, null, f));
-        assertEquals(provider.toEncrypted(session, null, f), provider.toEncrypted(session, null, f));
-    }
-}
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7ProviderTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8ProviderTest.java
similarity index 61%
rename from cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7ProviderTest.java
rename to cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8ProviderTest.java
index ee372bed4d0..866d98a1a0f 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7ProviderTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8ProviderTest.java
@@ -24,7 +24,8 @@
 import ch.cyberduck.core.Path;
 import ch.cyberduck.core.TestProtocol;
 import ch.cyberduck.core.cryptomator.CryptoDirectory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVaultTest;
 import ch.cyberduck.core.exception.BackgroundException;
 import ch.cyberduck.core.exception.NotfoundException;
 import ch.cyberduck.core.features.Read;
@@ -37,34 +38,36 @@
 
 import java.io.InputStream;
 import java.nio.charset.Charset;
+import java.security.SecureRandom;
 import java.util.EnumSet;
 
-import static ch.cyberduck.core.cryptomator.CryptoVault.VAULT_VERSION;
-import static ch.cyberduck.core.cryptomator.CryptoVaultTest.createJWT;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-public class CryptoDirectoryV7ProviderTest {
+public class CryptoDirectoryV8ProviderTest {
 
     @Test(expected = NotfoundException.class)
     public void testToEncryptedInvalidArgument() throws Exception {
         final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
-        final CryptoVault vault = new CryptoVault(home);
-        final CryptoDirectory provider = new CryptoDirectoryV7Provider(home, vault);
-        provider.toEncrypted(new NullSession(new Host(new TestProtocol())), null, new Path("/vault/f", EnumSet.of(Path.Type.file)));
+        final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_GCM);
+        final SecureRandom random = new SecureRandom();
+        final NullSession session = new NullSession(new Host(new TestProtocol()));
+        final CryptoDirectory provider = new CryptoDirectoryV8Provider(new CryptoVault(home), new CryptoFilenameV7Provider());
+        provider.toEncrypted(session, new Path("/vault/f", EnumSet.of(Path.Type.file)));
     }
 
     @Test(expected = NotfoundException.class)
     public void testToEncryptedInvalidPath() throws Exception {
         final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
-        final CryptoVault vault = new CryptoVault(home);
-        final CryptoDirectory provider = new CryptoDirectoryV7Provider(home, vault);
-        provider.toEncrypted(new NullSession(new Host(new TestProtocol())), null, new Path("/", EnumSet.of(Path.Type.directory)));
+        final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_GCM);
+        final SecureRandom random = new SecureRandom();
+        final NullSession session = new NullSession(new Host(new TestProtocol()));
+        final CryptoDirectory provider = new CryptoDirectoryV8Provider(new CryptoVault(home), new CryptoFilenameV7Provider());
+        provider.toEncrypted(session, new Path("/", EnumSet.of(Path.Type.directory)));
     }
 
     @Test
     public void testToEncryptedDirectory() throws Exception {
-        final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
         final NullSession session = new NullSession(new Host(new TestProtocol())) {
             @Override
             @SuppressWarnings("unchecked")
@@ -74,19 +77,20 @@ public  T _getFeature(final Class type) {
                         @Override
                         public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
                             final String masterKey = "{\n" +
-                                    "  \"scryptSalt\": \"NrC7QGG/ouc=\",\n" +
-                                    "  \"scryptCostParam\": 16384,\n" +
+                                    "  \"version\": 8,\n" +
+                                    "  \"scryptSalt\": \"RVAAirkArDU=\",\n" +
+                                    "  \"scryptCostParam\": 32768,\n" +
                                     "  \"scryptBlockSize\": 8,\n" +
-                                    "  \"primaryMasterKey\": \"Q7pGo1l0jmZssoQh9rXFPKJE9NIXvPbL+HcnVSR9CHdkeR8AwgFtcw==\",\n" +
-                                    "  \"hmacMasterKey\": \"xzBqT4/7uEcQbhHFLC0YmMy4ykVKbuvJEA46p1Xm25mJNuTc20nCbw==\",\n" +
-                                    "  \"versionMac\": \"hlNr3dz/CmuVajhaiGyCem9lcVIUjDfSMLhjppcXOrM=\",\n" +
-                                    "  \"version\": 8\n" +
+                                    "  \"primaryMasterKey\": \"+03NkJNWVsJ9Tb1CTpKhXyfINzjDirFFI+iJLOWIOySyxB+abpx34Q==\",\n" +
+                                    "  \"hmacMasterKey\": \"aMoDtn7Y6kIXxyHo2zl47p5jCYTlRnfx3l3AMgULmIDSYAxVAraSgg==\",\n" +
+                                    "  \"versionMac\": \"FzirA8UhwCmS5RsC4JvxbO+ZBxaCbIkzqD2Ocagd+A8=\"\n" +
                                     "}";
+
                             if("masterkey.cryptomator".equals(file.getName())) {
                                 return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
                             }
                             if("vault.cryptomator".equals(file.getName())) {
-                                return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault"), Charset.defaultCharset());
+                                return IOUtils.toInputStream(CryptoVaultTest.createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
                             }
                             throw new NotfoundException(String.format("%s not found", file.getName()));
                         }
@@ -100,17 +104,18 @@ public boolean offset(final Path file) {
                 return super._getFeature(type);
             }
         };
+        final Path home = new Path("/vault", EnumSet.of((Path.Type.directory)));
         final CryptoVault vault = new CryptoVault(home);
         vault.load(session, new DisabledPasswordCallback() {
             @Override
             public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
-                return new VaultCredentials("vault");
+                return new VaultCredentials("vault123");
             }
         });
-        final CryptoDirectory provider = new CryptoDirectoryV7Provider(home, vault);
-        assertNotNull(provider.toEncrypted(session, null, home));
+        final CryptoDirectory provider = new CryptoDirectoryV8Provider(vault, new CryptoFilenameV7Provider());
+        assertNotNull(provider.toEncrypted(session, home));
         final Path f = new Path("/vault/f", EnumSet.of(Path.Type.directory));
-        assertNotNull(provider.toEncrypted(session, null, f));
-        assertEquals(provider.toEncrypted(session, null, f), provider.toEncrypted(session, null, f));
+        assertNotNull(provider.toEncrypted(session, f));
+        assertEquals(provider.toEncrypted(session, f), provider.toEncrypted(session, f));
     }
 }
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVaultTest.java
similarity index 89%
rename from cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java
rename to cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVaultTest.java
index 0a9361a6de7..494dbf8b510 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVaultTest.java
@@ -1,7 +1,7 @@
-package ch.cyberduck.core.cryptomator;
+package ch.cyberduck.core.cryptomator.impl.v8;
 
 /*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
  * https://cyberduck.io/
  *
  * This program is free software; you can redistribute it and/or modify
@@ -25,6 +25,7 @@
 import ch.cyberduck.core.SerializerFactory;
 import ch.cyberduck.core.TestProtocol;
 import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cryptomator.CryptoInvalidFilesizeException;
 import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider;
 import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
 import ch.cyberduck.core.exception.BackgroundException;
@@ -39,10 +40,11 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 
 import org.apache.commons.io.IOUtils;
 import org.cryptomator.cryptolib.api.CryptorProvider;
-import org.cryptomator.cryptolib.api.Masterkey;
+import org.cryptomator.cryptolib.api.PerpetualMasterkey;
 import org.cryptomator.cryptolib.common.MasterkeyFile;
 import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
 import org.junit.Test;
@@ -60,7 +62,6 @@
 import com.auth0.jwt.JWT;
 import com.auth0.jwt.algorithms.Algorithm;
 
-import static ch.cyberduck.core.cryptomator.CryptoVault.VAULT_VERSION;
 import static org.junit.Assert.*;
 
 public class CryptoVaultTest {
@@ -89,7 +90,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
                                 return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
                             }
                             if("vault.cryptomator".equals(file.getName())) {
-                                return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
+                                return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
                             }
                             throw new NotfoundException(String.format("%s not found", file.getName()));
                         }
@@ -118,8 +119,10 @@ public Credentials prompt(final Host bookmark, final String title, final String
         assertEquals(vault.encrypt(session, home), vault.encrypt(session, home));
         final Path directory = new Path(home, "dir", EnumSet.of(Path.Type.directory));
         assertNull(directory.attributes().getVault());
-        assertEquals(home, vault.encrypt(session, directory).attributes().getVault());
-        assertEquals(home, directory.attributes().getVault());
+        assertEquals(home, vault.encrypt(session, directory).attributes().getVaultMetadata().root);
+        assertEquals(VaultMetadata.Type.V8, vault.encrypt(session, directory).attributes().getVaultMetadata().type);
+        assertEquals(home, directory.attributes().getVaultMetadata().root);
+        assertEquals(VaultMetadata.Type.V8, directory.attributes().getVaultMetadata().type);
         assertEquals(vault.encrypt(session, directory), vault.encrypt(session, directory));
         assertEquals(new Path(home, directory.getName(), EnumSet.of(Path.Type.directory, Path.Type.decrypted)), vault.decrypt(session, vault.encrypt(session, directory, true)));
         final Path placeholder = new Path(home, "placeholder", EnumSet.of(Path.Type.directory, Path.Type.placeholder));
@@ -171,7 +174,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
                                 return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
                             }
                             if("vault.cryptomator".equals(file.getName())) {
-                                return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
+                                return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
                             }
                             throw new NotfoundException(String.format("%s not found", file.getName()));
                         }
@@ -222,7 +225,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
                                 return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
                             }
                             if("vault.cryptomator".equals(file.getName())) {
-                                return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
+                                return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
                             }
                             throw new NotfoundException(String.format("%s not found", file.getName()));
                         }
@@ -272,7 +275,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
                                 return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
                             }
                             if("vault.cryptomator".equals(file.getName())) {
-                                return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
+                                return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
                             }
                             throw new NotfoundException(String.format("%s not found", file.getName()));
                         }
@@ -433,51 +436,6 @@ public Path mkdir(final Write writer, final Path folder, final TransferStatus st
         vault.create(session, null, new VaultCredentials("test"));
     }
 
-    @Test
-    public void testCleartextSizeV6() throws Exception {
-        final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
-        final NullSession session = new NullSession(new Host(new TestProtocol())) {
-            @Override
-            @SuppressWarnings("unchecked")
-            public  T _getFeature(final Class type) {
-                if(type == Directory.class) {
-                    return (T) new Directory() {
-
-                        @Override
-                        public Path mkdir(final Write writer, final Path folder, final TransferStatus status) {
-                            assertTrue(folder.equals(home) || folder.isChild(home));
-                            return folder;
-                        }
-                    };
-                }
-                return super._getFeature(type);
-            }
-        };
-        final CryptoVault vault = new CryptoVault(
-                home);
-        vault.create(session, null, new VaultCredentials("test"), 6);
-        // zero ciphertextFileSize
-        try {
-            vault.toCleartextSize(0L, 0);
-            fail();
-        }
-        catch(CryptoInvalidFilesizeException e) {
-        }
-        // ciphertextFileSize == headerSize
-        assertEquals(0L, vault.toCleartextSize(0L, vault.getFileHeaderCryptor().headerSize()));
-        // ciphertextFileSize == headerSize + 1
-        try {
-            vault.toCleartextSize(0L, vault.toCleartextSize(0L, vault.getFileHeaderCryptor().headerSize()) + 1);
-            fail();
-        }
-        catch(CryptoInvalidFilesizeException e) {
-        }
-        // ciphertextFileSize == headerSize + chunkHeaderSize + 1
-        assertEquals(1L, vault.toCleartextSize(0L, vault.getFileHeaderCryptor().headerSize() + 48 + 1));
-        // ciphertextFileSize == headerSize + (32768 + chunkHeaderSize) + (1 + chunkHeaderSize) + 1
-        assertEquals(32769L, vault.toCleartextSize(0L, vault.getFileHeaderCryptor().headerSize() + (32768 + 48) + (1 + 48)));
-    }
-
     @Test
     public void testCleartextSizeV8() throws Exception {
         final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
@@ -498,8 +456,7 @@ public Path mkdir(final Write writer, final Path folder, final TransferStatus st
                 return super._getFeature(type);
             }
         };
-        final CryptoVault vault = new CryptoVault(
-                home);
+        final CryptoVault vault = new CryptoVault(home);
         vault.create(session, null, new VaultCredentials("test"));
         // zero ciphertextFileSize
         try {
@@ -548,8 +505,7 @@ public boolean isSupported(final Path workdir, final String name) {
                 return super._getFeature(type);
             }
         };
-        final CryptoVault vault = new CryptoVault(
-                home);
+        final CryptoVault vault = new CryptoVault(home);
         vault.create(session, null, new VaultCredentials("test"));
         for(int i = 0; i < 26000000; i++) {
             assertEquals(i, vault.toCleartextSize(0L, vault.toCiphertextSize(0L, i)));
@@ -564,7 +520,7 @@ public static String createJWT(final String masterkeyCryptomator,
             final MasterkeyFile mkFile = MasterkeyFile.read(new StringReader(masterkeyCryptomator));
             final StringWriter writer = new StringWriter();
             mkFile.write(writer);
-            final Masterkey masterkey = new MasterkeyFileAccess(PreferencesFactory.get().getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8),
+            final PerpetualMasterkey masterkey = new MasterkeyFileAccess(PreferencesFactory.get().getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8),
                     FastSecureRandomProvider.get().provide()).load(new ByteArrayInputStream(writer.getBuffer().toString().getBytes(StandardCharsets.UTF_8)), passphrase);
             final Algorithm algorithm = Algorithm.HMAC256(masterkey.getEncoded());
             return JWT.create()
diff --git a/ctera/pom.xml b/ctera/pom.xml
index 249b2e6f7db..89a386c51c4 100644
--- a/ctera/pom.xml
+++ b/ctera/pom.xml
@@ -18,7 +18,7 @@
     
         ch.cyberduck
         parent
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     ctera
     jar
diff --git a/deepbox/pom.xml b/deepbox/pom.xml
index 1d7dd42f32d..8d9df7170c9 100644
--- a/deepbox/pom.xml
+++ b/deepbox/pom.xml
@@ -19,7 +19,7 @@
     
         parent
         ch.cyberduck
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     deepbox
 
diff --git a/defaults/pom.xml b/defaults/pom.xml
index c27c855ec83..0cf1e56d401 100644
--- a/defaults/pom.xml
+++ b/defaults/pom.xml
@@ -19,7 +19,7 @@
     
         parent
         ch.cyberduck
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     defaults
 
diff --git a/defaults/src/main/resources/default.properties b/defaults/src/main/resources/default.properties
index 710dec5d6d8..926ca6c5b80 100644
--- a/defaults/src/main/resources/default.properties
+++ b/defaults/src/main/resources/default.properties
@@ -703,13 +703,14 @@ terminal.command.ssh=ssh -t {0} {1}@{2} -p {3} \"cd {4} && exec \\$SHELL -l\"
 threading.pool.size.max=20
 threading.pool.keepalive.seconds=60
 cryptomator.enable=true
-cryptomator.vault.version=8
+cryptomator.vault.default=v8
 cryptomator.vault.autodetect=true
 cryptomator.vault.autodetect.filecount=10
 # Load and add to registry when vault is referenced in file attributes
 cryptomator.vault.autoload=true
 cryptomator.vault.masterkey.filename=masterkey.cryptomator
 cryptomator.vault.config.filename=vault.cryptomator
+cryptomator.vault.config.filename.uvf=vault.uvf
 cryptomator.vault.pepper=
 cryptomator.vault.skip.regex=dirid.c9r
 cryptomator.cache.size=1000
diff --git a/defaults/src/main/resources/default/log4j.xml b/defaults/src/main/resources/default/log4j.xml
index a02f90d3f10..ce4787cd17e 100644
--- a/defaults/src/main/resources/default/log4j.xml
+++ b/defaults/src/main/resources/default/log4j.xml
@@ -1,11 +1,11 @@
 
 
 
     
-        
+        
+            
+        
     
     
         
         
-        
-            
+        
+        
+            
         
     
 
diff --git a/dracoon/pom.xml b/dracoon/pom.xml
index a3e4aec8bfc..516c68d76a1 100644
--- a/dracoon/pom.xml
+++ b/dracoon/pom.xml
@@ -19,7 +19,7 @@
     
         parent
         ch.cyberduck
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     dracoon
 
diff --git a/dropbox/pom.xml b/dropbox/pom.xml
index 40bf3f4ffbf..0e1fe38d080 100644
--- a/dropbox/pom.xml
+++ b/dropbox/pom.xml
@@ -18,7 +18,7 @@
     
         parent
         ch.cyberduck
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     4.0.0
     dropbox
diff --git a/dropbox/src/test/java/ch/cyberduck/core/AbstractDropboxTest.java b/dropbox/src/test/java/ch/cyberduck/core/AbstractDropboxTest.java
index 361a6586246..552af6ebe8c 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/AbstractDropboxTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/AbstractDropboxTest.java
@@ -15,12 +15,12 @@
  * GNU General Public License for more details.
  */
 
-import ch.cyberduck.core.cryptomator.CryptoVault;
 import ch.cyberduck.core.dropbox.DropboxProtocol;
 import ch.cyberduck.core.dropbox.DropboxSession;
 import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
 import ch.cyberduck.core.ssl.DefaultX509KeyManager;
 import ch.cyberduck.core.ssl.DefaultX509TrustManager;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.VaultTest;
 
 import org.junit.After;
@@ -38,11 +38,11 @@ public class AbstractDropboxTest extends VaultTest {
 
     @Parameterized.Parameters(name = "vaultVersion = {0}")
     public static Object[] data() {
-        return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+        return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
     }
 
     @Parameterized.Parameter
-    public int vaultVersion;
+    public VaultMetadata.Type vaultVersion;
 
     @After
     public void disconnect() throws Exception {
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
index 84f25f9340d..1060e833a55 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
@@ -46,12 +46,14 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.core.worker.CopyWorker;
 import ch.cyberduck.core.worker.DeleteWorker;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.RandomUtils;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -66,7 +68,6 @@
 import com.dropbox.core.v2.files.Metadata;
 
 import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
@@ -78,8 +79,8 @@ public void testCopyFile() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         final Path target = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         final byte[] content = RandomUtils.nextBytes(40500);
@@ -104,8 +105,8 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
         final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         final Path targetFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path target = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         new CryptoTouchFeature<>(session, new DefaultTouchFeature(
@@ -121,16 +122,17 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
         new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session);
     }
 
+    //TODO
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new DefaultHomeFinderService(session).find();
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path source = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
         final Path targetFolder = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
         final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         new CryptoTouchFeature<>(session, new DefaultTouchFeature(
@@ -152,8 +154,8 @@ public void testCopyFolder() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -187,8 +189,8 @@ public void testCopyFileIntoVault() throws Exception {
         assertTrue(new DropboxFindFeature(session).find(cleartextFile));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -213,8 +215,8 @@ public void testCopyDirectoryIntoVault() throws Exception {
         new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), cleartextFile, new TransferStatus());
         assertTrue(new DropboxFindFeature(session).find(cleartextFolder));
         assertTrue(new DropboxFindFeature(session).find(cleartextFile));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         // move directory into vault
@@ -238,8 +240,8 @@ public void testCopyFileOutsideVault() throws Exception {
         new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), clearFolder, new TransferStatus());
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -264,8 +266,8 @@ public void testCopyDirectoryOutsideVault() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CryptoDropboxSingleTransferWorkerTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CryptoDropboxSingleTransferWorkerTest.java
index ac5582d36aa..b91cc053ab9 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CryptoDropboxSingleTransferWorkerTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CryptoDropboxSingleTransferWorkerTest.java
@@ -50,6 +50,7 @@
 import ch.cyberduck.core.transfer.UploadTransfer;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.core.worker.SingleTransferWorker;
 import ch.cyberduck.test.IntegrationTest;
 
@@ -91,8 +92,8 @@ public void testUpload() throws Exception {
         final OutputStream out2 = localFile2.getOutputStream(false);
         IOUtils.write(content, out2);
         out2.close();
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final Transfer t = new UploadTransfer(new Host(new TestProtocol()), Collections.singletonList(new TransferItem(dir1, localDirectory1)), new NullFilter<>());
         assertTrue(new SingleTransferWorker(session, session, t, new TransferOptions(), new TransferSpeedometer(t), new DisabledTransferPrompt() {
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java
index 3b6fa9869fd..f322ba2ef5d 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java
@@ -35,8 +35,10 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -47,7 +49,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
@@ -57,8 +58,8 @@ public class DropboxDirectoryFeatureTest extends AbstractDropboxTest {
     public void testMakeDirectoryEncrypted() throws Exception {
         final Path home = new DefaultHomeFinderService(session).find();
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final Path test = cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
                 cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
@@ -69,13 +70,13 @@ public void testMakeDirectoryEncrypted() throws Exception {
     }
 
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testMakeDirectoryLongFilenameEncrypted() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new DefaultHomeFinderService(session).find();
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path test = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
                 cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), test, new TransferStatus());
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java
index ef2ce97f56a..46d80a19ac0 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java
@@ -33,6 +33,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.junit.Test;
@@ -56,8 +57,8 @@ public class DropboxListServiceTest extends AbstractDropboxTest {
     public void testListCryptomator() throws Exception {
         final Path home = new DefaultHomeFinderService(session).find();
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         assertTrue(new CryptoListService(session, new DropboxListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
         final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java
index d40ecf497ab..d5efcce3615 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java
@@ -39,6 +39,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.junit.Test;
@@ -64,8 +65,8 @@ public void testMove() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
                 cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), folder, new TransferStatus());
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java
index 1c7bfd7cc38..99bf044b0e9 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java
@@ -35,8 +35,10 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -49,19 +51,19 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
 public class DropboxTouchFeatureTest extends AbstractDropboxTest {
 
+    //TODO
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testTouchLongFilenameEncrypted() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new DefaultHomeFinderService(session).find();
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final TransferStatus status = new TransferStatus();
         final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
@@ -74,12 +76,12 @@ public void testTouchLongFilenameEncrypted() throws Exception {
     }
 
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testTouchLongFilenameEncryptedDefaultFeature() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new DefaultHomeFinderService(session).find();
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final TransferStatus status = new TransferStatus();
         final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxWriteFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxWriteFeatureTest.java
index 13545619d4c..bb942fd195b 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxWriteFeatureTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxWriteFeatureTest.java
@@ -40,6 +40,7 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.test.IntegrationTest;
 
 import org.apache.commons.lang3.RandomUtils;
@@ -72,8 +73,8 @@ public void testWrite() throws Exception {
         final Path home = new DefaultHomeFinderService(session).find();
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         final CryptoWriteFeature writer = new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator);
         final FileHeader header = cryptomator.getFileHeaderCryptor().create();
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
index 47f1208e211..e71563b8795 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
@@ -42,9 +42,11 @@
 import ch.cyberduck.core.transfer.TransferStatus;
 import ch.cyberduck.core.vault.DefaultVaultRegistry;
 import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
 import ch.cyberduck.core.worker.MoveWorker;
 import ch.cyberduck.test.IntegrationTest;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -55,7 +57,6 @@
 import java.util.EnumSet;
 
 import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
 
 @Category(IntegrationTest.class)
 @RunWith(value = Parameterized.class)
@@ -67,8 +68,8 @@ public void testMoveSameFolderCryptomator() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         final Path target = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch(
                 new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus());
@@ -87,8 +88,8 @@ public void testMoveToDifferentFolderCryptomator() throws Exception {
         final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
         final Path targetFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path target = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch(
                 new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus());
@@ -103,16 +104,17 @@ public void testMoveToDifferentFolderCryptomator() throws Exception {
         cryptomator.getFeature(session, Delete.class, new DropboxDeleteFeature(session)).delete(Arrays.asList(target, targetFolder, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
     }
 
+    //TODO
     @Test
+    @Ignore(value = "Filename shortening not yet implemented")
     public void testMoveToDifferentFolderLongFilenameCryptomator() throws Exception {
-        assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
         final Path home = new DefaultHomeFinderService(session).find();
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path source = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
         final Path targetFolder = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
         final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch(
                 new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus());
@@ -133,8 +135,8 @@ public void testMoveFolder() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
                 cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), folder, new TransferStatus());
@@ -173,8 +175,8 @@ public void testMoveFileIntoVault() throws Exception {
         assertTrue(new DefaultFindFeature(session).find(clearFile));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -199,8 +201,8 @@ public void testMoveDirectoryIntoVault() throws Exception {
         new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), clearFile, new TransferStatus());
         assertTrue(new DefaultFindFeature(session).find(clearFolder));
         assertTrue(new DefaultFindFeature(session).find(clearFile));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         // move directory into vault
@@ -224,8 +226,8 @@ public void testMoveFileOutsideVault() throws Exception {
         new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), clearFolder, new TransferStatus());
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -251,8 +253,8 @@ public void testMoveDirectoryOutsideVault() throws Exception {
         final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
         final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+        final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+                new VaultMetadata(vault, vaultVersion));
         final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
         session.withRegistry(registry);
         cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
diff --git a/eue/pom.xml b/eue/pom.xml
index c264b8473a0..d499f136cc3 100644
--- a/eue/pom.xml
+++ b/eue/pom.xml
@@ -19,7 +19,7 @@
     
         parent
         ch.cyberduck
-        9.3.0-SNAPSHOT
+        9.3.0.uvfdraft-SNAPSHOT
     
     eue
     jar
diff --git a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java
deleted file mode 100644
index 883a1996c04..00000000000
--- a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package ch.cyberduck.core.cryptomator;
-
-/*
- * Copyright (c) 2002-2021 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.AbstractPath;
-import ch.cyberduck.core.AlphanumericRandomStringService;
-import ch.cyberduck.core.BytecountStreamListener;
-import ch.cyberduck.core.DisabledConnectionCallback;
-import ch.cyberduck.core.DisabledLoginCallback;
-import ch.cyberduck.core.DisabledPasswordCallback;
-import ch.cyberduck.core.DisabledProgressListener;
-import ch.cyberduck.core.Local;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.cryptomator.features.CryptoReadFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoUploadFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
-import ch.cyberduck.core.eue.AbstractEueSessionTest;
-import ch.cyberduck.core.eue.EueAttributesFinderFeature;
-import ch.cyberduck.core.eue.EueDeleteFeature;
-import ch.cyberduck.core.eue.EueDirectoryFeature;
-import ch.cyberduck.core.eue.EueFindFeature;
-import ch.cyberduck.core.eue.EueReadFeature;
-import ch.cyberduck.core.eue.EueResourceIdProvider;
-import ch.cyberduck.core.eue.EueSingleUploadService;
-import ch.cyberduck.core.eue.EueWriteFeature;
-import ch.cyberduck.core.features.AttributesFinder;
-import ch.cyberduck.core.features.Delete;
-import ch.cyberduck.core.features.Find;
-import ch.cyberduck.core.io.BandwidthThrottle;
-import ch.cyberduck.core.io.StreamCopier;
-import ch.cyberduck.core.transfer.TransferStatus;
-import ch.cyberduck.core.vault.DefaultVaultRegistry;
-import ch.cyberduck.core.vault.VaultCredentials;
-import ch.cyberduck.test.IntegrationTest;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.RandomUtils;
-import org.cryptomator.cryptolib.api.FileHeader;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.UUID;
-
-import static org.junit.Assert.*;
-
-@Category(IntegrationTest.class)
-@RunWith(value = Parameterized.class)
-public class EueSingleUploadServiceTest extends AbstractEueSessionTest {
-
-    @Test
-    public void testUploadVault() throws Exception {
-        final EueResourceIdProvider fileid = new EueResourceIdProvider(session);
-        final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L));
-        final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
-        final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
-        session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
-        final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
-        final byte[] content = RandomUtils.nextBytes(502400);
-        IOUtils.write(content, local.getOutputStream(false));
-        final TransferStatus writeStatus = new TransferStatus();
-        final FileHeader header = cryptomator.getFileHeaderCryptor().create();
-        writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header));
-        writeStatus.setLength(content.length);
-        final BytecountStreamListener count = new BytecountStreamListener();
-        final CryptoUploadFeature feature = new CryptoUploadFeature<>(session,
-                new EueSingleUploadService(session, fileid),
-                cryptomator);
-        feature.upload(new CryptoWriteFeature<>(session, new EueWriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null);
-        assertEquals(content.length, count.getSent());
-        assertTrue(writeStatus.isComplete());
-        assertTrue(cryptomator.getFeature(session, Find.class, new EueFindFeature(session, fileid)).find(test));
-        assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new EueAttributesFinderFeature(session, fileid)).find(test).getSize());
-        final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length);
-        final TransferStatus readStatus = new TransferStatus().setLength(content.length);
-        final InputStream in = new CryptoReadFeature(session, new EueReadFeature(session, fileid), cryptomator).read(test, readStatus, new DisabledConnectionCallback());
-        new StreamCopier(readStatus, readStatus).transfer(in, buffer);
-        assertArrayEquals(content, buffer.toByteArray());
-        cryptomator.getFeature(session, Delete.class, new EueDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
-        local.delete();
-    }
-}
diff --git a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java
deleted file mode 100644
index 9855b7e9cd1..00000000000
--- a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package ch.cyberduck.core.cryptomator;
-
-/*
- * Copyright (c) 2002-2021 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.AlphanumericRandomStringService;
-import ch.cyberduck.core.BytecountStreamListener;
-import ch.cyberduck.core.DisabledConnectionCallback;
-import ch.cyberduck.core.DisabledLoginCallback;
-import ch.cyberduck.core.DisabledPasswordCallback;
-import ch.cyberduck.core.DisabledProgressListener;
-import ch.cyberduck.core.Local;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.cryptomator.features.CryptoBulkFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoReadFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoUploadFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
-import ch.cyberduck.core.eue.AbstractEueSessionTest;
-import ch.cyberduck.core.eue.EueAttributesFinderFeature;
-import ch.cyberduck.core.eue.EueDeleteFeature;
-import ch.cyberduck.core.eue.EueDirectoryFeature;
-import ch.cyberduck.core.eue.EueFindFeature;
-import ch.cyberduck.core.eue.EueReadFeature;
-import ch.cyberduck.core.eue.EueResourceIdProvider;
-import ch.cyberduck.core.eue.EueThresholdUploadService;
-import ch.cyberduck.core.eue.EueWriteFeature;
-import ch.cyberduck.core.features.AttributesFinder;
-import ch.cyberduck.core.features.Delete;
-import ch.cyberduck.core.features.Find;
-import ch.cyberduck.core.io.BandwidthThrottle;
-import ch.cyberduck.core.io.StreamCopier;
-import ch.cyberduck.core.shared.DisabledBulkFeature;
-import ch.cyberduck.core.transfer.Transfer;
-import ch.cyberduck.core.transfer.TransferItem;
-import ch.cyberduck.core.transfer.TransferStatus;
-import ch.cyberduck.core.vault.DefaultVaultRegistry;
-import ch.cyberduck.core.vault.VaultCredentials;
-import ch.cyberduck.test.IntegrationTest;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.RandomUtils;
-import org.cryptomator.cryptolib.api.FileHeader;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Map;
-import java.util.UUID;
-
-import static org.junit.Assert.*;
-
-@Category(IntegrationTest.class)
-@RunWith(value = Parameterized.class)
-public class EueThresholdUploadServiceTest extends AbstractEueSessionTest {
-
-    @Test
-    public void testUploadVault() throws Exception {
-        final EueResourceIdProvider fileid = new EueResourceIdProvider(session);
-        final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L));
-        final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
-        final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
-        final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
-        session.withRegistry(registry);
-        final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
-        final byte[] content = RandomUtils.nextBytes(8840780);
-        IOUtils.write(content, local.getOutputStream(false));
-        final TransferStatus writeStatus = new TransferStatus();
-        final FileHeader header = cryptomator.getFileHeaderCryptor().create();
-        writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header));
-        writeStatus.setLength(content.length);
-        final BytecountStreamListener count = new BytecountStreamListener();
-        final CryptoUploadFeature feature = new CryptoUploadFeature<>(session,
-                new EueThresholdUploadService(session, fileid, registry),
-                cryptomator);
-        feature.upload(new CryptoWriteFeature<>(session, new EueWriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback());
-        assertEquals(content.length, count.getSent());
-        assertTrue(writeStatus.isComplete());
-        assertTrue(cryptomator.getFeature(session, Find.class, new EueFindFeature(session, fileid)).find(test));
-        assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new EueAttributesFinderFeature(session, fileid)).find(test).getSize());
-        final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length);
-        final TransferStatus readStatus = new TransferStatus().setLength(content.length);
-        final InputStream in = new CryptoReadFeature(session, new EueReadFeature(session, fileid), cryptomator).read(test, readStatus, new DisabledConnectionCallback());
-        new StreamCopier(readStatus, readStatus).transfer(in, buffer);
-        assertArrayEquals(content, buffer.toByteArray());
-        cryptomator.getFeature(session, Delete.class, new EueDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
-        local.delete();
-    }
-
-    @Test
-    public void testUploadVaultWithBulkFeature() throws Exception {
-        final EueResourceIdProvider fileid = new EueResourceIdProvider(session);
-        final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L));
-        final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
-        final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
-        final CryptoVault cryptomator = new CryptoVault(vault);
-        cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
-        final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
-        session.withRegistry(registry);
-        final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
-        final byte[] content = RandomUtils.nextBytes(50240000);
-        IOUtils.write(content, local.getOutputStream(false));
-        final TransferStatus writeStatus = new TransferStatus();
-        final FileHeader header = cryptomator.getFileHeaderCryptor().create();
-        writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header));
-        writeStatus.setLength(content.length);
-        final CryptoBulkFeature