Index: package-lock.json
===================================================================
--- package-lock.json	(revision ecbfa417315102faa2b0b25640f48006f7b172c7)
+++ package-lock.json	(revision 603d2461f6fb57f0422bea17aad09eef157f788f)
@@ -1,5 +1,5 @@
 {
   "name": "klonkt",
-  "version": "1.0.0",
+  "version": "1.6.0-dev",
   "lockfileVersion": 3,
   "requires": true,
@@ -7,8 +7,10 @@
     "": {
       "name": "klonkt",
-      "version": "1.0.0",
+      "version": "1.6.0-dev",
       "license": "AGPL-3.0-or-later",
       "dependencies": {
         "@resvg/resvg-js": "^2.6.2",
+        "@simplewebauthn/browser": "^13.3.0",
+        "@simplewebauthn/server": "^13.3.2",
         "bcryptjs": "^2.4.3",
         "better-sqlite3": "^11.3.0",
@@ -64,4 +66,184 @@
       }
     },
+    "node_modules/@hexagon/base64": {
+      "version": "1.1.28",
+      "resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.1.28.tgz",
+      "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==",
+      "license": "MIT"
+    },
+    "node_modules/@levischuck/tiny-cbor": {
+      "version": "0.2.11",
+      "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz",
+      "integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==",
+      "license": "MIT"
+    },
+    "node_modules/@peculiar/asn1-android": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.8.0.tgz",
+      "integrity": "sha512-skLbS+IOGv1lUgDqtChr8xvtvEr3HMse/JGBaL2r1J1o/n7a8wqOrovMtlRq/UXLhxvmLaONP67hwtshgzwfzA==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-schema": "^2.8.0",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-cms": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.8.0.tgz",
+      "integrity": "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-schema": "^2.8.0",
+        "@peculiar/asn1-x509": "^2.8.0",
+        "@peculiar/asn1-x509-attr": "^2.8.0",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-csr": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.8.0.tgz",
+      "integrity": "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-schema": "^2.8.0",
+        "@peculiar/asn1-x509": "^2.8.0",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-ecc": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.8.0.tgz",
+      "integrity": "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-schema": "^2.8.0",
+        "@peculiar/asn1-x509": "^2.8.0",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-pfx": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.8.0.tgz",
+      "integrity": "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-cms": "^2.8.0",
+        "@peculiar/asn1-pkcs8": "^2.8.0",
+        "@peculiar/asn1-rsa": "^2.8.0",
+        "@peculiar/asn1-schema": "^2.8.0",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-pkcs8": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.8.0.tgz",
+      "integrity": "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-schema": "^2.8.0",
+        "@peculiar/asn1-x509": "^2.8.0",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-pkcs9": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.8.0.tgz",
+      "integrity": "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-cms": "^2.8.0",
+        "@peculiar/asn1-pfx": "^2.8.0",
+        "@peculiar/asn1-pkcs8": "^2.8.0",
+        "@peculiar/asn1-schema": "^2.8.0",
+        "@peculiar/asn1-x509": "^2.8.0",
+        "@peculiar/asn1-x509-attr": "^2.8.0",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-rsa": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.8.0.tgz",
+      "integrity": "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-schema": "^2.8.0",
+        "@peculiar/asn1-x509": "^2.8.0",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-schema": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz",
+      "integrity": "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/utils": "^2.0.2",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-x509": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.8.0.tgz",
+      "integrity": "sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-schema": "^2.8.0",
+        "@peculiar/utils": "^2.0.2",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/asn1-x509-attr": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.8.0.tgz",
+      "integrity": "sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-schema": "^2.8.0",
+        "@peculiar/asn1-x509": "^2.8.0",
+        "asn1js": "^3.0.10",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/utils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz",
+      "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/@peculiar/x509": {
+      "version": "1.14.3",
+      "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz",
+      "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==",
+      "license": "MIT",
+      "dependencies": {
+        "@peculiar/asn1-cms": "^2.6.0",
+        "@peculiar/asn1-csr": "^2.6.0",
+        "@peculiar/asn1-ecc": "^2.6.0",
+        "@peculiar/asn1-pkcs9": "^2.6.0",
+        "@peculiar/asn1-rsa": "^2.6.0",
+        "@peculiar/asn1-schema": "^2.6.0",
+        "@peculiar/asn1-x509": "^2.6.0",
+        "pvtsutils": "^1.3.6",
+        "reflect-metadata": "^0.2.2",
+        "tslib": "^2.8.1",
+        "tsyringe": "^4.10.0"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      }
+    },
     "node_modules/@resvg/resvg-js": {
       "version": "2.6.2",
@@ -279,4 +461,29 @@
       }
     },
+    "node_modules/@simplewebauthn/browser": {
+      "version": "13.3.0",
+      "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.3.0.tgz",
+      "integrity": "sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==",
+      "license": "MIT"
+    },
+    "node_modules/@simplewebauthn/server": {
+      "version": "13.3.2",
+      "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.3.2.tgz",
+      "integrity": "sha512-KEDhfcGP1PAKRVSDjA3npTQFqS2b/srm+ipoNBNHdkzrHAlaRQUTE+a5f4ywsx6thxAw1NU2rYcLEY1949RGbQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@hexagon/base64": "^1.1.27",
+        "@levischuck/tiny-cbor": "^0.2.2",
+        "@peculiar/asn1-android": "^2.6.0",
+        "@peculiar/asn1-ecc": "^2.6.1",
+        "@peculiar/asn1-rsa": "^2.6.1",
+        "@peculiar/asn1-schema": "^2.6.0",
+        "@peculiar/asn1-x509": "^2.6.1",
+        "@peculiar/x509": "^1.14.3"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      }
+    },
     "node_modules/@types/node": {
       "version": "10.17.60",
@@ -344,4 +551,18 @@
       "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
       "license": "MIT"
+    },
+    "node_modules/asn1js": {
+      "version": "3.0.10",
+      "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz",
+      "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "pvtsutils": "^1.3.6",
+        "pvutils": "^1.1.5",
+        "tslib": "^2.8.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
     },
     "node_modules/async": {
@@ -1776,4 +1997,22 @@
       }
     },
+    "node_modules/pvtsutils": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz",
+      "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/pvutils": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz",
+      "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
     "node_modules/qs": {
       "version": "6.15.1",
@@ -1852,4 +2091,10 @@
         "node": ">= 6"
       }
+    },
+    "node_modules/reflect-metadata": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+      "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+      "license": "Apache-2.0"
     },
     "node_modules/safe-buffer": {
@@ -2154,4 +2399,28 @@
       }
     },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD"
+    },
+    "node_modules/tsyringe": {
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz",
+      "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^1.9.3"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/tsyringe/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+      "license": "0BSD"
+    },
     "node_modules/tunnel-agent": {
       "version": "0.6.0",
