diff --git a/src/_secureaccesstokenauthorizer/model.py b/src/_secureaccesstokenauthorizer/model.py
index 2e852c42f1f1e365eba39e4ff945b3774e9fb0c0..bcdc4d636432856fc03015ff2d03a1152dfbfb50 100644
--- a/src/_secureaccesstokenauthorizer/model.py
+++ b/src/_secureaccesstokenauthorizer/model.py
@@ -17,26 +17,25 @@ This module implements models (in the MVC sense) for the client side of
 the storage plugin.
 """
 
-from os import (
-    makedirs,
-    listdir,
-)
-from errno import (
-    EEXIST,
-    ENOENT,
+from functools import (
+    wraps,
 )
 from json import (
     loads,
     dumps,
 )
+
+from sqlite3 import (
+    connect,
+)
+
 import attr
 
-# XXX
-from allmydata.node import (
-    _Config,
-    MissingConfigEntry,
+from twisted.python.filepath import (
+    FilePath,
 )
 
+
 class StoreAddError(Exception):
     def __init__(self, reason):
         self.reason = reason
@@ -47,73 +46,126 @@ class StoreDirectoryError(Exception):
         self.reason = reason
 
 
+class SchemaError(TypeError):
+    pass
+
+
+CONFIG_DB_NAME = u"privatestorageio-satauthz-v1.sqlite3"
+
+def open_and_initialize(path):
+    try:
+        path.parent().makedirs(ignoreExistingDirectory=True)
+    except OSError as e:
+        raise StoreDirectoryError(e)
+
+    conn = connect(
+        path.asBytesMode().path,
+        isolation_level="IMMEDIATE",
+    )
+    with conn:
+        cursor = conn.cursor()
+        cursor.execute(
+            """
+            CREATE TABLE IF NOT EXISTS [version] AS SELECT 1 AS [version]
+            """
+        )
+        cursor.execute(
+            """
+            SELECT [version] FROM [version]
+            """
+        )
+        expected = [(1,)]
+        version = cursor.fetchall()
+        if version != expected:
+            raise SchemaError(
+                "Unexpected database schema version.  Expected {}.  Got {}.".format(
+                    expected,
+                    version,
+                ),
+            )
+
+        cursor.execute(
+            """
+            CREATE TABLE IF NOT EXISTS [payment-references] (
+                number text,
+
+                PRIMARY KEY(number)
+            )
+            """,
+        )
+    return conn
+
+
+def with_cursor(f):
+    @wraps(f)
+    def with_cursor(self, *a, **kw):
+        with self._connection:
+            return f(self, self._connection.cursor(), *a, **kw)
+    return with_cursor
+
+
 @attr.s(frozen=True)
 class PaymentReferenceStore(object):
     """
     This class implements persistence for payment references.
 
-    :ivar _Config node_config: The Tahoe-LAFS node configuration object for
+    :ivar allmydata.node._Config node_config: The Tahoe-LAFS node configuration object for
         the node that owns the persisted payment preferences.
     """
-    _CONFIG_DIR = u"privatestorageio-satauthz-v1"
-    node_config = attr.ib(type=_Config)
-
-    def _config_key(self, prn):
-        return u"{}/{}.prn+json".format(self._CONFIG_DIR, prn)
-
-    def _prn(self, config_key):
-        if config_key.endswith(u".prn+json"):
-            return config_key[:-len(u".prn+json")]
-        raise ValueError("{} does not look like a config key".format(config_key))
-
-    def _read_pr_json(self, prn):
-        private_config_item = self._config_key(prn)
-        try:
-            return self.node_config.get_private_config(private_config_item)
-        except MissingConfigEntry:
+    database_path = attr.ib(type=FilePath)
+    _connection = attr.ib()
+
+    @classmethod
+    def from_node_config(cls, node_config):
+        db_path = FilePath(node_config.get_private_path(CONFIG_DB_NAME))
+        conn = open_and_initialize(
+            db_path,
+        )
+        return cls(
+            db_path,
+            conn,
+        )
+
+    @with_cursor
+    def get(self, cursor, prn):
+        cursor.execute(
+            """
+            SELECT
+                ([number])
+            FROM
+                [payment-references]
+            WHERE
+                [number] = ?
+            """,
+            (prn,),
+        )
+        refs = cursor.fetchall()
+        if len(refs) == 0:
             raise KeyError(prn)
+        return PaymentReference(refs[0][0])
+
+    @with_cursor
+    def add(self, cursor, prn):
+        cursor.execute(
+            """
+            INSERT OR IGNORE INTO [payment-references] VALUES (?)
+            """,
+            (prn,)
+        )
+
+    @with_cursor
+    def list(self, cursor):
+        cursor.execute(
+            """
+            SELECT ([number]) FROM [payment-references]
+            """,
+        )
+        refs = cursor.fetchall()
 
-    def _write_pr_json(self, prn, pr_json):
-        private_config_item = self._config_key(prn)
-        # XXX Need an API to be able to avoid touching the filesystem directly
-        # here.
-        container = self.node_config.get_private_path(self._CONFIG_DIR)
-        try:
-            makedirs(container)
-        except EnvironmentError as e:
-            if EEXIST != e.errno:
-                raise StoreDirectoryError(e)
-        try:
-            self.node_config.write_private_config(private_config_item, pr_json)
-        except Exception as e:
-            raise StoreAddError(e)
-
-    def get(self, prn):
-        payment_reference_json = self._read_pr_json(prn)
-        return PaymentReference.from_json(payment_reference_json)
-
-    def add(self, prn):
-        # XXX Not *exactly* atomic is it?  Probably want a
-        # write_private_config_if_not_exists or something.
-        try:
-            self._read_pr_json(prn)
-        except KeyError:
-            self._write_pr_json(prn, PaymentReference(prn).to_json())
-
-    def list(self):
-        # XXX Need an API to be able to avoid touching the filesystem directly
-        # here.
-        container = self.node_config.get_private_path(self._CONFIG_DIR)
-        try:
-            children = listdir(container)
-        except EnvironmentError as e:
-            if ENOENT != e.errno:
-                raise
-            children = []
         return list(
-            PaymentReference(self._prn(config_key))
-            for config_key
-            in children
+            PaymentReference(number)
+            for (number,)
+            in refs
         )
 
 
diff --git a/src/_secureaccesstokenauthorizer/resource.py b/src/_secureaccesstokenauthorizer/resource.py
index 49b37f5b8b9038d352ac469749d289da75389523..cd9b06b9eef7e8fb771d88a9c794f6f15aa39a8a 100644
--- a/src/_secureaccesstokenauthorizer/resource.py
+++ b/src/_secureaccesstokenauthorizer/resource.py
@@ -60,7 +60,7 @@ def from_configuration(node_config):
     :return IResource: The root of the resource hierarchy presented by the
         client side of the plugin.
     """
-    store = PaymentReferenceStore(node_config)
+    store = PaymentReferenceStore.from_node_config(node_config)
     controller = PaymentController(store)
     root = Resource()
     root.putChild(
diff --git a/src/_secureaccesstokenauthorizer/tests/test_model.py b/src/_secureaccesstokenauthorizer/tests/test_model.py
index 1159133d7a6fe8d0fd8834ad423af7e7b0e94551..0d3772afe2e2873baa520bf0998fb79df28b183c 100644
--- a/src/_secureaccesstokenauthorizer/tests/test_model.py
+++ b/src/_secureaccesstokenauthorizer/tests/test_model.py
@@ -17,7 +17,6 @@ Tests for ``_secureaccesstokenauthorizer.model``.
 """
 
 from os import (
-    chmod,
     mkdir,
 )
 from errno import (
@@ -73,7 +72,7 @@ class PaymentReferenceStoreTests(TestCase):
         """
         tempdir = self.useFixture(TempDir())
         config = get_config(tempdir.join(b"node"), b"tub.port")
-        store = PaymentReferenceStore(config)
+        store = PaymentReferenceStore.from_node_config(config)
         self.assertThat(
             lambda: store.get(prn),
             raises(KeyError),
@@ -88,7 +87,7 @@ class PaymentReferenceStoreTests(TestCase):
         """
         tempdir = self.useFixture(TempDir())
         config = get_config(tempdir.join(b"node"), b"tub.port")
-        store = PaymentReferenceStore(config)
+        store = PaymentReferenceStore.from_node_config(config)
         store.add(prn)
         payment_reference = store.get(prn)
         self.assertThat(
@@ -106,7 +105,7 @@ class PaymentReferenceStoreTests(TestCase):
         """
         tempdir = self.useFixture(TempDir())
         config = get_config(tempdir.join(b"node"), b"tub.port")
-        store = PaymentReferenceStore(config)
+        store = PaymentReferenceStore.from_node_config(config)
         store.add(prn)
         store.add(prn)
         payment_reference = store.get(prn)
@@ -128,7 +127,7 @@ class PaymentReferenceStoreTests(TestCase):
         tempdir = self.useFixture(TempDir())
         nodedir = tempdir.join(b"node")
         config = get_config(nodedir, b"tub.port")
-        store = PaymentReferenceStore(config)
+        store = PaymentReferenceStore.from_node_config(config)
 
         for prn in prns:
             store.add(prn)
@@ -142,59 +141,24 @@ class PaymentReferenceStoreTests(TestCase):
         )
 
 
-    @given(tahoe_configs(), payment_reference_numbers(), payment_reference_numbers())
-    def test_unwriteable_store_directory(self, get_config, prn_a, prn_b):
-        """
-        If the underlying directory in the node configuration is not writeable
-        then ``PaymentReferenceStore.add`` raises ``StoreAddError``.
-        """
-        assume(prn_a != prn_b)
-        tempdir = self.useFixture(TempDir())
-        nodedir = tempdir.join(b"node")
-        config = get_config(nodedir, b"tub.port")
-        store = PaymentReferenceStore(config)
-        # Initialize the underlying directory.
-        store.add(prn_a)
-        # Mess it up
-        chmod(config.get_private_path(store._CONFIG_DIR), 0o500)
-
-        self.assertThat(
-            lambda: store.add(prn_b),
-            Raises(
-                AfterPreprocessing(
-                    lambda (type, exc, tb): exc,
-                    MatchesAll(
-                        IsInstance(StoreAddError),
-                        MatchesStructure(
-                            reason=MatchesAll(
-                                IsInstance(IOError),
-                                MatchesStructure(
-                                    errno=Equals(EACCES),
-                                ),
-                            ),
-                        ),
-                    ),
-                ),
-            ),
-        )
-
     @given(tahoe_configs(), payment_reference_numbers())
     def test_uncreateable_store_directory(self, get_config, prn):
         """
         If the underlying directory in the node configuration cannot be created
-        then ``PaymentReferenceStore.add`` raises ``StoreDirectoryError``.
+        then ``PaymentReferenceStore.from_node_config`` raises
+        ``StoreDirectoryError``.
         """
         tempdir = self.useFixture(TempDir())
         nodedir = tempdir.join(b"node")
-        config = get_config(nodedir, b"tub.port")
-        store = PaymentReferenceStore(config)
 
         # Create the node directory without permission to create the
         # underlying directory.
         mkdir(nodedir, 0o500)
 
+        config = get_config(nodedir, b"tub.port")
+
         self.assertThat(
-            lambda: store.add(prn),
+            lambda: PaymentReferenceStore.from_node_config(config),
             Raises(
                 AfterPreprocessing(
                     lambda (type, exc, tb): exc,
diff --git a/src/_secureaccesstokenauthorizer/tests/test_plugin.py b/src/_secureaccesstokenauthorizer/tests/test_plugin.py
index 99d1db405ba341394313ba4a72f268c92259c9af..a7b9dceca6ac9f768d8d9b0f7722f4b25dc7a643 100644
--- a/src/_secureaccesstokenauthorizer/tests/test_plugin.py
+++ b/src/_secureaccesstokenauthorizer/tests/test_plugin.py
@@ -20,6 +20,10 @@ from zope.interface import (
     implementer,
 )
 
+from fixtures import (
+    TempDir,
+)
+
 from testtools import (
     TestCase,
 )
@@ -234,11 +238,14 @@ class ClientResourceTests(TestCase):
     ``IFoolscapStoragePlugin.get_client_resource``.
     """
     @given(tahoe_configs())
-    def test_interface(self, tahoe_config):
+    def test_interface(self, get_config):
         """
         ``get_client_resource`` returns an object that provides ``IResource``.
         """
+        tempdir = self.useFixture(TempDir())
+        nodedir = tempdir.join(b"node")
+        config = get_config(nodedir, b"tub.port")
         self.assertThat(
-            storage_server.get_client_resource(tahoe_config),
+            storage_server.get_client_resource(config),
             Provides([IResource]),
         )