diff --git a/src/_zkapauthorizer/_plugin.py b/src/_zkapauthorizer/_plugin.py
index e400e87a29308d7daf3466c2f74653108cea169f..5db792867d005da2ee03fc2864287b514abbc55c 100644
--- a/src/_zkapauthorizer/_plugin.py
+++ b/src/_zkapauthorizer/_plugin.py
@@ -17,6 +17,14 @@ The Twisted plugin that glues the Zero-Knowledge Access Pass system into
 Tahoe-LAFS.
 """
 
+from weakref import (
+    WeakValueDictionary,
+)
+
+from functools import (
+    partial,
+)
+
 import attr
 
 from zope.interface import (
@@ -37,8 +45,8 @@ from .api import (
     ZKAPAuthorizerStorageClient,
 )
 
-from ._storage_server import (
-    TOKEN_LENGTH,
+from .model import (
+    VoucherStore,
 )
 
 from .resource import (
@@ -56,14 +64,37 @@ class AnnounceableStorageServer(object):
     storage_server = attr.ib()
 
 
-
+@attr.s
 @implementer(IFoolscapStoragePlugin)
 class ZKAPAuthorizer(object):
     """
     A storage plugin which provides a token-based access control mechanism on
     top of the Tahoe-LAFS built-in storage server interface.
+
+    :ivar WeakValueDictionary _stores: A mapping from node directories to this
+        plugin's database connections for those nodes.  The existence of any
+        kind of attribute to reference database connections (not so much the
+        fact that it is a WeakValueDictionary; if it were just a weakref the
+        same would be true) probably reflects an error in the interface which
+        forces different methods to use instance state to share a database
+        connection.
     """
-    name = u"privatestorageio-zkapauthz-v1"
+    name = attr.ib(default=u"privatestorageio-zkapauthz-v1")
+    _stores = attr.ib(default=attr.Factory(WeakValueDictionary))
+
+    def _get_store(self, node_config):
+        """
+        :return VoucherStore: The database for the given node.  At most one
+            connection is made to the database per ``ZKAPAuthorizer`` instance.
+        """
+        key =  node_config.get_config_path()
+        try:
+            s = self._stores[key]
+        except KeyError:
+            s = VoucherStore.from_node_config(node_config)
+            self._stores[key] = s
+        return s
+
 
     def get_storage_server(self, configuration, get_anonymous_storage_server):
         announcement = {}
@@ -79,11 +110,13 @@ class ZKAPAuthorizer(object):
         )
 
 
-    def get_storage_client(self, configuration, announcement, get_rref):
+    def get_storage_client(self, node_config, announcement, get_rref):
         return succeed(
             ZKAPAuthorizerStorageClient(
                 get_rref,
-                lambda: [b"x" * TOKEN_LENGTH],
+                # TODO: Make the caller figure out the correct number of
+                # passes to extract.
+                partial(self._get_store(node_config).extract_passes, 1),
             )
         )
 
@@ -91,5 +124,6 @@ class ZKAPAuthorizer(object):
     def get_client_resource(self, node_config):
         return resource_from_configuration(
             node_config,
+            store=self._get_store(node_config),
             redeemer=DummyRedeemer(),
         )
diff --git a/src/_zkapauthorizer/tests/test_plugin.py b/src/_zkapauthorizer/tests/test_plugin.py
index 72c017ace8af653dfac8198446e3aa98753265ed..25fefb5b6369306e9834dd1d352ccd2dfe4c4f97 100644
--- a/src/_zkapauthorizer/tests/test_plugin.py
+++ b/src/_zkapauthorizer/tests/test_plugin.py
@@ -31,10 +31,16 @@ from testtools.matchers import (
     Always,
     Contains,
     AfterPreprocessing,
+    Equals,
 )
 from testtools.twistedsupport import (
     succeeded,
 )
+from testtools.twistedsupport._deferred import (
+    # I'd rather use https://twistedmatrix.com/trac/ticket/8900 but efforts
+    # there appear to have stalled.
+    extract_result,
+)
 
 from hypothesis import (
     given,
@@ -47,6 +53,9 @@ from foolscap.ipb import (
     IReferenceable,
     IRemotelyCallable,
 )
+from foolscap.referenceable import (
+    LocalReferenceable,
+)
 
 from allmydata.interfaces import (
     IFoolscapStoragePlugin,
@@ -68,10 +77,19 @@ from twisted.plugins.zkapauthorizer import (
     storage_server,
 )
 
+from ..model import (
+    VoucherStore,
+)
+
 from .strategies import (
     tahoe_configs,
     configurations,
     announcements,
+    vouchers,
+    random_tokens,
+    zkaps,
+    storage_indexes,
+    lease_renew_secrets,
 )
 from .matchers import (
     Provides,
@@ -88,7 +106,7 @@ def get_anonymous_storage_server():
 
 
 def get_rref():
-    return None
+    return LocalReferenceable(None)
 
 
 class PluginTests(TestCase):
@@ -214,14 +232,20 @@ class ClientPluginTests(TestCase):
     Tests for the plugin's implementation of
     ``IFoolscapStoragePlugin.get_storage_client``.
     """
-    @given(configurations(), announcements())
-    def test_interface(self, configuration, announcement):
+    @given(tahoe_configs(), announcements())
+    def test_interface(self, get_config, announcement):
         """
         ``get_storage_client`` returns a ``Deferred`` that fires with an object
         which provides ``IStorageServer``.
         """
+        tempdir = self.useFixture(TempDir())
+        node_config = get_config(
+            tempdir.join(b"node"),
+            b"tub.port",
+        )
+
         storage_client_deferred = storage_server.get_storage_client(
-            configuration,
+            node_config,
             announcement,
             get_rref,
         )
@@ -232,6 +256,63 @@ class ClientPluginTests(TestCase):
         )
 
 
+    @given(
+        tahoe_configs(),
+        announcements(),
+        vouchers(),
+        random_tokens(),
+        zkaps(),
+        storage_indexes(),
+        lease_renew_secrets(),
+    )
+    def test_passes_extracted(
+            self,
+            get_config,
+            announcement,
+            voucher,
+            token,
+            zkap,
+            storage_index,
+            renew_secret,
+    ):
+        """
+        The ``ZKAPAuthorizerStorageServer`` returned by ``get_storage_client``
+        extracts passes from the plugin database.
+        """
+        tempdir = self.useFixture(TempDir())
+        node_config = get_config(
+            tempdir.join(b"node"),
+            b"tub.port",
+        )
+
+        store = VoucherStore.from_node_config(node_config)
+        store.add(voucher, [token])
+        store.insert_passes_for_voucher(voucher, [zkap])
+
+        storage_client_deferred = storage_server.get_storage_client(
+            node_config,
+            announcement,
+            get_rref,
+        )
+
+        storage_client = extract_result(storage_client_deferred)
+
+        # This is hooked up to a garbage reference.  We don't care about its
+        # _result_, anyway, right now.
+        d = storage_client.renew_lease(
+            storage_index,
+            renew_secret,
+        )
+        d.addBoth(lambda ignored: None)
+
+        # There should be no passes left to extract.
+        remaining = store.extract_passes(1)
+        self.assertThat(
+            remaining,
+            Equals([]),
+        )
+
+
 class ClientResourceTests(TestCase):
     """
     Tests for the plugin's implementation of