diff --git a/src/_secureaccesstokenauthorizer/_storage_client.py b/src/_secureaccesstokenauthorizer/_storage_client.py
index 9a77fbcf87a69c34fde371d6c4cea4fa7e7c3c8d..ff86d26d39ace4797923a9ff16b217e3ae4145ed 100644
--- a/src/_secureaccesstokenauthorizer/_storage_client.py
+++ b/src/_secureaccesstokenauthorizer/_storage_client.py
@@ -117,3 +117,31 @@ class SecureAccessTokenAuthorizerStorageClient(object):
             shnum,
             reason,
         )
+
+    def slot_testv_and_readv_and_writev(
+            self,
+            storage_index,
+            secrets,
+            tw_vectors,
+            r_vector,
+    ):
+        return self._rref.callRemote(
+            "slot_testv_and_readv_and_writev",
+            storage_index,
+            secrets,
+            tw_vectors,
+            r_vector,
+        )
+
+    def slot_readv(
+            self,
+            storage_index,
+            shares,
+            r_vector,
+    ):
+        return self._rref.callRemote(
+            "slot_readv",
+            storage_index,
+            shares,
+            r_vector,
+        )
diff --git a/src/_secureaccesstokenauthorizer/_storage_server.py b/src/_secureaccesstokenauthorizer/_storage_server.py
index 6264b310a93065b93c112546721038d8797a87bb..78a4800b350ee9370bc11551a6c5808a5de2f9fe 100644
--- a/src/_secureaccesstokenauthorizer/_storage_server.py
+++ b/src/_secureaccesstokenauthorizer/_storage_server.py
@@ -126,6 +126,12 @@ class SecureAccessTokenAuthorizerStorageServer(Referenceable):
     def remote_advise_corrupt_share(self, *a, **kw):
         return self._original.remote_advise_corrupt_share(*a, **kw)
 
+    def remote_slot_testv_and_readv_and_writev(self, *a, **kw):
+        return self._original.remote_slot_testv_and_readv_and_writev(*a, **kw)
+
+    def remote_slot_readv(self, *a, **kw):
+        return self._original.remote_slot_readv(*a, **kw)
+
 # I don't understand why this is required.
 # SecureAccessTokenAuthorizerStorageServer is-a Referenceable.  It seems like
 # the built in adapter should take care of this case.
diff --git a/src/_secureaccesstokenauthorizer/tests/strategies.py b/src/_secureaccesstokenauthorizer/tests/strategies.py
index 945ad614e0a1ed8f3c4022008e641e3e983f74e3..e3f5e5f15f9df710d00838156998114f1dbcc1d2 100644
--- a/src/_secureaccesstokenauthorizer/tests/strategies.py
+++ b/src/_secureaccesstokenauthorizer/tests/strategies.py
@@ -17,16 +17,21 @@ Hypothesis strategies for property testing.
 """
 
 from hypothesis.strategies import (
+    one_of,
     just,
     binary,
     integers,
     sets,
+    lists,
+    tuples,
+    dictionaries,
 )
 
 from allmydata.interfaces import (
     StorageIndex,
     LeaseRenewSecret,
     LeaseCancelSecret,
+    WriteEnablerSecret,
 )
 
 def configurations():
@@ -66,6 +71,16 @@ def lease_cancel_secrets():
     )
 
 
+def write_enabler_secrets():
+    """
+    Build Tahoe-LAFS write enabler secrets.
+    """
+    return binary(
+        min_size=WriteEnablerSecret.minLength,
+        max_size=WriteEnablerSecret.maxLength,
+    )
+
+
 def sharenums():
     """
     Build Tahoe-LAFS share numbers.
@@ -91,8 +106,103 @@ def sizes():
     """
     Build Tahoe-LAFS share sizes.
     """
+    return integers(
+        # Size 0 data isn't data, it's nothing.
+        min_value=1,
+        # Just for practical purposes...
+        max_value=2 ** 16,
+    )
+
+
+def offsets():
+    """
+    Build Tahoe-LAFS share offsets.
+    """
     return integers(
         min_value=0,
         # Just for practical purposes...
         max_value=2 ** 16,
     )
+
+
+def bytes_for_share(sharenum, size):
+    """
+    :return bytes: marginally distinctive bytes of a certain length for the
+        given share number
+    """
+    if 0 <= sharenum <= 255:
+        return (unichr(sharenum) * size).encode("latin-1")
+    raise ValueError("Sharenum must be between 0 and 255 inclusive.")
+
+
+def shares():
+    """
+    Build Tahoe-LAFS share data.
+    """
+    return tuples(
+        sharenums(),
+        sizes()
+    ).map(
+        lambda (num, size): bytes_for_share(num, size),
+    )
+
+
+def data_vectors():
+    """
+    Build Tahoe-LAFS data vectors.
+    """
+    return lists(
+        tuples(
+            offsets(),
+            shares(),
+        ),
+        # An empty data vector doesn't make much sense.  If you have no data
+        # to write, you should probably use slot_readv instead.  Also,
+        # Tahoe-LAFS explodes if you pass an empty data vector -
+        # storage/server.py, OSError(ENOENT) from `os.listdir(bucketdir)`.
+        min_size=1,
+        # Just for practical purposes...
+        max_size=8,
+    )
+
+
+def test_vectors():
+    """
+    Build Tahoe-LAFS test vectors.
+    """
+    return lists(
+        # XXX TODO
+        just(None),
+        min_size=0,
+        max_size=0,
+    )
+
+
+def test_and_write_vectors():
+    """
+    Build Tahoe-LAFS test and write vectors for a single share.
+    """
+    return tuples(
+        test_vectors(),
+        data_vectors(),
+        one_of(
+            just(None),
+            sizes(),
+        ),
+    )
+
+
+def test_and_write_vectors_for_shares():
+    """
+    Build Tahoe-LAFS test and write vectors for a number of shares.
+    """
+    return dictionaries(
+        sharenums(),
+        test_and_write_vectors(),
+        # An empty dictionary wouldn't make much sense.  And it provokes a
+        # NameError from Tahoe, storage/server.py:479, `new_length` referenced
+        # before assignment.
+        min_size=1,
+        # Just for practical purposes...
+        max_size=8,
+    )
diff --git a/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py b/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py
index cc980e6854c70b274d2527909a30c5dc10fccc1e..8e17be297c48c6898e2808f5719731920cc7235e 100644
--- a/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py
+++ b/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py
@@ -15,6 +15,7 @@
 """
 Tests for communication between the client and server components.
 """
+
 import attr
 
 from fixtures import (
@@ -38,6 +39,7 @@ from testtools.twistedsupport._deferred import (
 from hypothesis import (
     given,
     assume,
+    note,
 )
 from hypothesis.strategies import (
     tuples,
@@ -62,9 +64,13 @@ from .strategies import (
     storage_indexes,
     lease_renew_secrets,
     lease_cancel_secrets,
+    write_enabler_secrets,
     sharenums,
     sharenum_sets,
     sizes,
+    test_and_write_vectors_for_shares,
+    # Not really a strategy...
+    bytes_for_share,
 )
 
 from ..api import (
@@ -72,16 +78,6 @@ from ..api import (
     SecureAccessTokenAuthorizerStorageClient,
 )
 
-def bytes_for_share(sharenum, size):
-    """
-    Generate marginally distinctive bytes of a certain length for the given
-    share number.
-    """
-    if 0 <= sharenum <= 255:
-        return (unichr(sharenum) * size).encode("latin-1")
-    raise ValueError("Sharenum must be between 0 and 255 inclusive.")
-
-
 class AnonymousStorageServer(Fixture):
     def _setUp(self):
         self.tempdir = self.useFixture(TempDir()).join(b"storage")
@@ -310,6 +306,88 @@ class ShareTests(TestCase):
             HasLength(1),
         )
 
+    @given(
+        storage_index=storage_indexes(),
+        secrets=tuples(
+            write_enabler_secrets(),
+            lease_renew_secrets(),
+            lease_cancel_secrets(),
+        ),
+        test_and_write_vectors_for_shares=test_and_write_vectors_for_shares(),
+    )
+    def test_create_mutable(self, storage_index, secrets, test_and_write_vectors_for_shares):
+        """
+        Mutable share data written using *slot_testv_and_readv_and_writev* can be
+        read back.
+        """
+        # Hypothesis causes our storage server to be used many times.  Clean
+        # up between iterations.
+        cleanup_storage_server(self.anonymous_storage_server)
+
+        wrote, read = extract_result(
+            self.client.slot_testv_and_readv_and_writev(
+                storage_index,
+                secrets=secrets,
+                tw_vectors=test_and_write_vectors_for_shares,
+                r_vector=[],
+            ),
+        )
+
+        self.assertThat(
+            wrote,
+            Equals(True),
+            u"Server rejected a write to a new mutable storage index",
+        )
+
+        self.assertThat(
+            read,
+            Equals({}),
+            u"Server gave back read results when we asked for none.",
+        )
+
+        for sharenum, (test_vector, write_vector, new_length) in test_and_write_vectors_for_shares.items():
+            r_vector = list(map(write_vector_to_read_vector, write_vector))
+            read = extract_result(
+                self.client.slot_readv(
+                    storage_index,
+                    shares=[sharenum],
+                    r_vector=r_vector,
+                ),
+            )
+            note("read vector {}".format(r_vector))
+            # Create a buffer and pile up all the write operations in it.
+            # This lets us make correct assertions about overlapping writes.
+            length = max(
+                offset + len(data)
+                for (offset, data)
+                in write_vector
+            )
+            expected = b"\x00" * length
+            for (offset, data) in write_vector:
+                expected = expected[:offset] + data + expected[offset + len(data):]
+            if new_length is not None and new_length < length:
+                expected = expected[:new_length]
+            self.assertThat(
+                read,
+                Equals({sharenum: list(
+                    # Get the expected value out of our scratch buffer.
+                    expected[offset:offset + len(data)]
+                    for (offset, data)
+                    in write_vector
+                )}),
+                u"Server didn't reliably read back data just written for share {}".format(
+                    sharenum,
+                ),
+            )
+
+
+def write_vector_to_read_vector(write_vector):
+    """
+    Create a read vector which will read back the data written by the given
+    write vector.
+    """
+    return (write_vector[0], len(write_vector[1]))
+
 
 def write_toy_shares(
         storage_server,