cursedbelt-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/dist/server/activity/index.d.ts +36 -0
- package/dist/server/activity/index.js +36 -0
- package/dist/server/activity/migrations.d.ts +21 -0
- package/dist/server/activity/migrations.js +55 -0
- package/dist/server/activity/shareSource.d.ts +11 -0
- package/dist/server/activity/shareSource.js +45 -0
- package/dist/server/activity/store.d.ts +123 -0
- package/dist/server/activity/store.js +178 -0
- package/dist/server/app.d.ts +56 -0
- package/dist/server/app.js +45 -0
- package/dist/server/audit/index.d.ts +2 -0
- package/dist/server/audit/index.js +2 -0
- package/dist/server/audit/migrations.d.ts +2 -0
- package/dist/server/audit/migrations.js +79 -0
- package/dist/server/audit/sqliteSink.d.ts +36 -0
- package/dist/server/audit/sqliteSink.js +95 -0
- package/dist/server/audit/sqliteSink.test.d.ts +1 -0
- package/dist/server/audit/sqliteSink.test.js +180 -0
- package/dist/server/auth/cookie.d.ts +22 -0
- package/dist/server/auth/cookie.js +35 -0
- package/dist/server/auth/csrf.d.ts +42 -0
- package/dist/server/auth/csrf.js +36 -0
- package/dist/server/auth/fileTokenSigner.d.ts +10 -0
- package/dist/server/auth/fileTokenSigner.js +22 -0
- package/dist/server/auth/getOnly.d.ts +20 -0
- package/dist/server/auth/getOnly.js +27 -0
- package/dist/server/auth/index.d.ts +12 -0
- package/dist/server/auth/index.js +15 -0
- package/dist/server/auth/jwt.d.ts +18 -0
- package/dist/server/auth/jwt.js +56 -0
- package/dist/server/auth/loginThrottle.d.ts +118 -0
- package/dist/server/auth/loginThrottle.js +204 -0
- package/dist/server/auth/middleware.d.ts +14 -0
- package/dist/server/auth/middleware.js +47 -0
- package/dist/server/auth/password.d.ts +14 -0
- package/dist/server/auth/password.js +65 -0
- package/dist/server/auth/sessionStore.d.ts +19 -0
- package/dist/server/auth/sessionStore.js +221 -0
- package/dist/server/auth/totp.d.ts +34 -0
- package/dist/server/auth/totp.js +81 -0
- package/dist/server/auth/types.d.ts +133 -0
- package/dist/server/auth/types.js +1 -0
- package/dist/server/auth/vault.d.ts +27 -0
- package/dist/server/auth/vault.js +57 -0
- package/dist/server/context.d.ts +35 -0
- package/dist/server/context.js +1 -0
- package/dist/server/ctgr.d.ts +32 -0
- package/dist/server/ctgr.js +69 -0
- package/dist/server/db/kysely.d.ts +14 -0
- package/dist/server/db/kysely.js +16 -0
- package/dist/server/db/plumbingTypes.d.ts +116 -0
- package/dist/server/db/plumbingTypes.js +12 -0
- package/dist/server/dropzone/duplicates.d.ts +82 -0
- package/dist/server/dropzone/duplicates.js +90 -0
- package/dist/server/dropzone/index.d.ts +13 -0
- package/dist/server/dropzone/index.js +13 -0
- package/dist/server/dropzone/scan.d.ts +166 -0
- package/dist/server/dropzone/scan.js +272 -0
- package/dist/server/errors.d.ts +7 -0
- package/dist/server/errors.js +7 -0
- package/dist/server/guard/argon2Binding.d.ts +97 -0
- package/dist/server/guard/argon2Binding.js +138 -0
- package/dist/server/guard/index.d.ts +4 -0
- package/dist/server/guard/index.js +6 -0
- package/dist/server/guard/kdfWorker.d.ts +101 -0
- package/dist/server/guard/kdfWorker.js +301 -0
- package/dist/server/guard/kdfWorkerEntry.d.ts +1 -0
- package/dist/server/guard/kdfWorkerEntry.js +15 -0
- package/dist/server/guard/passwordVerifier.d.ts +65 -0
- package/dist/server/guard/passwordVerifier.js +122 -0
- package/dist/server/guard/revocationStore.d.ts +132 -0
- package/dist/server/guard/revocationStore.js +211 -0
- package/dist/server/guard/sessionSigner.d.ts +132 -0
- package/dist/server/guard/sessionSigner.js +303 -0
- package/dist/server/guard/verifier.d.ts +21 -0
- package/dist/server/guard/verifier.js +1 -0
- package/dist/server/incidents/channels.d.ts +161 -0
- package/dist/server/incidents/channels.js +357 -0
- package/dist/server/incidents/checks.d.ts +61 -0
- package/dist/server/incidents/checks.js +55 -0
- package/dist/server/incidents/index.d.ts +51 -0
- package/dist/server/incidents/index.js +51 -0
- package/dist/server/incidents/migrations.d.ts +20 -0
- package/dist/server/incidents/migrations.js +58 -0
- package/dist/server/incidents/notifier.d.ts +79 -0
- package/dist/server/incidents/notifier.js +141 -0
- package/dist/server/incidents/reporter.d.ts +60 -0
- package/dist/server/incidents/reporter.js +49 -0
- package/dist/server/incidents/routes.d.ts +91 -0
- package/dist/server/incidents/routes.js +202 -0
- package/dist/server/incidents/store.d.ts +75 -0
- package/dist/server/incidents/store.js +176 -0
- package/dist/server/incidents/types.d.ts +107 -0
- package/dist/server/incidents/types.js +32 -0
- package/dist/server/index.d.ts +25 -0
- package/dist/server/index.js +53 -0
- package/dist/server/jobs/fireQueue.d.ts +12 -0
- package/dist/server/jobs/fireQueue.js +37 -0
- package/dist/server/jobs/index.d.ts +6 -0
- package/dist/server/jobs/index.js +10 -0
- package/dist/server/jobs/logger.d.ts +10 -0
- package/dist/server/jobs/logger.js +14 -0
- package/dist/server/jobs/plainjobAdapter.d.ts +19 -0
- package/dist/server/jobs/plainjobAdapter.js +48 -0
- package/dist/server/jobs/polling.d.ts +17 -0
- package/dist/server/jobs/polling.js +79 -0
- package/dist/server/jobs/runner.d.ts +24 -0
- package/dist/server/jobs/runner.js +186 -0
- package/dist/server/jobs/timeout.d.ts +28 -0
- package/dist/server/jobs/timeout.js +47 -0
- package/dist/server/jobs/types.d.ts +167 -0
- package/dist/server/jobs/types.js +12 -0
- package/dist/server/master-lock/guard.d.ts +53 -0
- package/dist/server/master-lock/guard.js +205 -0
- package/dist/server/master-lock/index.d.ts +21 -0
- package/dist/server/master-lock/index.js +21 -0
- package/dist/server/master-lock/lockPage.d.ts +34 -0
- package/dist/server/master-lock/lockPage.js +281 -0
- package/dist/server/master-lock/masterLock.d.ts +294 -0
- package/dist/server/master-lock/masterLock.js +523 -0
- package/dist/server/master-lock/principals.d.ts +76 -0
- package/dist/server/master-lock/principals.js +157 -0
- package/dist/server/master-lock/seed.d.ts +47 -0
- package/dist/server/master-lock/seed.js +93 -0
- package/dist/server/master-lock/store.d.ts +19 -0
- package/dist/server/master-lock/store.js +20 -0
- package/dist/server/media-bun/image.d.ts +7 -0
- package/dist/server/media-bun/image.js +96 -0
- package/dist/server/media-bun/index.d.ts +4 -0
- package/dist/server/media-bun/index.js +10 -0
- package/dist/server/media-bun/streaming.d.ts +86 -0
- package/dist/server/media-bun/streaming.js +237 -0
- package/dist/server/media-bun/video.d.ts +35 -0
- package/dist/server/media-bun/video.js +181 -0
- package/dist/server/metrics/metricsBuffer.d.ts +39 -0
- package/dist/server/metrics/metricsBuffer.js +36 -0
- package/dist/server/metrics/normalizeRoute.d.ts +5 -0
- package/dist/server/metrics/normalizeRoute.js +58 -0
- package/dist/server/middleware/correlationId.d.ts +9 -0
- package/dist/server/middleware/correlationId.js +20 -0
- package/dist/server/middleware/corsAllowlist.d.ts +38 -0
- package/dist/server/middleware/corsAllowlist.js +37 -0
- package/dist/server/middleware/errorEnvelope.d.ts +17 -0
- package/dist/server/middleware/errorEnvelope.js +32 -0
- package/dist/server/middleware/index.d.ts +9 -0
- package/dist/server/middleware/index.js +10 -0
- package/dist/server/middleware/localOrigin.d.ts +71 -0
- package/dist/server/middleware/localOrigin.js +119 -0
- package/dist/server/middleware/rateLimit.d.ts +56 -0
- package/dist/server/middleware/rateLimit.js +86 -0
- package/dist/server/middleware/requestLogger.d.ts +29 -0
- package/dist/server/middleware/requestLogger.js +47 -0
- package/dist/server/middleware/securityHeaders.d.ts +29 -0
- package/dist/server/middleware/securityHeaders.js +44 -0
- package/dist/server/middleware/zValidator.d.ts +29 -0
- package/dist/server/middleware/zValidator.js +57 -0
- package/dist/server/migration/runner.d.ts +42 -0
- package/dist/server/migration/runner.js +70 -0
- package/dist/server/migration/types.d.ts +16 -0
- package/dist/server/migration/types.js +1 -0
- package/dist/server/net/outboundGuard.d.ts +30 -0
- package/dist/server/net/outboundGuard.js +199 -0
- package/dist/server/notifications/hub.d.ts +67 -0
- package/dist/server/notifications/hub.js +187 -0
- package/dist/server/notifications/index.d.ts +47 -0
- package/dist/server/notifications/index.js +46 -0
- package/dist/server/notifications/migrations.d.ts +13 -0
- package/dist/server/notifications/migrations.js +20 -0
- package/dist/server/notifications/routes.d.ts +58 -0
- package/dist/server/notifications/routes.js +113 -0
- package/dist/server/notifications/service.d.ts +58 -0
- package/dist/server/notifications/service.js +41 -0
- package/dist/server/paths/appDataHome.d.ts +19 -0
- package/dist/server/paths/appDataHome.js +68 -0
- package/dist/server/private-media/crypto.d.ts +37 -0
- package/dist/server/private-media/crypto.js +111 -0
- package/dist/server/private-media/index-store.d.ts +31 -0
- package/dist/server/private-media/index-store.js +36 -0
- package/dist/server/private-media/mountPrivateRoutes.d.ts +26 -0
- package/dist/server/private-media/mountPrivateRoutes.js +219 -0
- package/dist/server/private-media/pipeline.d.ts +37 -0
- package/dist/server/private-media/pipeline.js +97 -0
- package/dist/server/private-media/unlock.d.ts +52 -0
- package/dist/server/private-media/unlock.js +55 -0
- package/dist/server/resume/index.d.ts +2 -0
- package/dist/server/resume/index.js +2 -0
- package/dist/server/resume/routes.d.ts +41 -0
- package/dist/server/resume/routes.js +50 -0
- package/dist/server/resume/store.d.ts +37 -0
- package/dist/server/resume/store.js +38 -0
- package/dist/server/retention/archive.d.ts +78 -0
- package/dist/server/retention/archive.js +284 -0
- package/dist/server/retention/fleet.d.ts +50 -0
- package/dist/server/retention/fleet.js +66 -0
- package/dist/server/retention/index.d.ts +38 -0
- package/dist/server/retention/index.js +38 -0
- package/dist/server/retention/policy.d.ts +263 -0
- package/dist/server/retention/policy.js +264 -0
- package/dist/server/retention/store.d.ts +101 -0
- package/dist/server/retention/store.js +251 -0
- package/dist/server/retention/sweeper.d.ts +69 -0
- package/dist/server/retention/sweeper.js +289 -0
- package/dist/server/retention/target.d.ts +143 -0
- package/dist/server/retention/target.js +438 -0
- package/dist/server/search/index.d.ts +2 -0
- package/dist/server/search/index.js +6 -0
- package/dist/server/search/pgSearchProvider.d.ts +91 -0
- package/dist/server/search/pgSearchProvider.js +330 -0
- package/dist/server/search/pgSearchProvider.pg.test.d.ts +1 -0
- package/dist/server/search/pgSearchProvider.pg.test.js +338 -0
- package/dist/server/search/pgSearchSchema.d.ts +81 -0
- package/dist/server/search/pgSearchSchema.js +89 -0
- package/dist/server/sharing/index.d.ts +18 -0
- package/dist/server/sharing/index.js +16 -0
- package/dist/server/sharing/migrations.d.ts +6 -0
- package/dist/server/sharing/migrations.js +79 -0
- package/dist/server/sharing/router.d.ts +111 -0
- package/dist/server/sharing/router.js +93 -0
- package/dist/server/sharing/sql.d.ts +67 -0
- package/dist/server/sharing/sql.js +88 -0
- package/dist/server/sharing/store.d.ts +173 -0
- package/dist/server/sharing/store.js +484 -0
- package/dist/server/sqlite/backup.d.ts +48 -0
- package/dist/server/sqlite/backup.js +97 -0
- package/dist/server/sqlite/browse.d.ts +42 -0
- package/dist/server/sqlite/browse.js +84 -0
- package/dist/server/sqlite/export.d.ts +32 -0
- package/dist/server/sqlite/export.js +42 -0
- package/dist/server/sqlite/fts5.d.ts +106 -0
- package/dist/server/sqlite/fts5.js +154 -0
- package/dist/server/sqlite/health.d.ts +44 -0
- package/dist/server/sqlite/health.js +52 -0
- package/dist/server/sqlite/import.d.ts +36 -0
- package/dist/server/sqlite/import.js +108 -0
- package/dist/server/sqlite/index.d.ts +9 -0
- package/dist/server/sqlite/index.js +15 -0
- package/dist/server/sqlite/readonlySql.d.ts +15 -0
- package/dist/server/sqlite/readonlySql.js +65 -0
- package/dist/server/sqlite/stats.d.ts +22 -0
- package/dist/server/sqlite/stats.js +32 -0
- package/dist/server/sqlite/viewer.d.ts +17 -0
- package/dist/server/sqlite/viewer.js +56 -0
- package/dist/server/storage/binaryServerProcessing.d.ts +23 -0
- package/dist/server/storage/binaryServerProcessing.js +81 -0
- package/dist/server/storage/binaryServerStore.d.ts +3 -0
- package/dist/server/storage/binaryServerStore.js +95 -0
- package/dist/server/storage/diskStore.d.ts +2 -0
- package/dist/server/storage/diskStore.js +88 -0
- package/dist/server/storage/files/adapter.d.ts +33 -0
- package/dist/server/storage/files/adapter.js +203 -0
- package/dist/server/storage/files/catalogue.d.ts +72 -0
- package/dist/server/storage/files/catalogue.js +1033 -0
- package/dist/server/storage/files/chunkedUpload.d.ts +56 -0
- package/dist/server/storage/files/chunkedUpload.js +70 -0
- package/dist/server/storage/files/fileRoutes.d.ts +51 -0
- package/dist/server/storage/files/fileRoutes.js +405 -0
- package/dist/server/storage/files/index.d.ts +9 -0
- package/dist/server/storage/files/index.js +11 -0
- package/dist/server/storage/files/migrations.d.ts +2 -0
- package/dist/server/storage/files/migrations.js +163 -0
- package/dist/server/storage/files/mimeByExt.d.ts +1 -0
- package/dist/server/storage/files/mimeByExt.js +13 -0
- package/dist/server/storage/files/stack.d.ts +136 -0
- package/dist/server/storage/files/stack.js +132 -0
- package/dist/server/storage/files/storageOps.d.ts +54 -0
- package/dist/server/storage/files/storageOps.js +110 -0
- package/dist/server/storage/files/types.d.ts +261 -0
- package/dist/server/storage/files/types.js +22 -0
- package/dist/server/storage/imageThumbnail.d.ts +3 -0
- package/dist/server/storage/imageThumbnail.js +50 -0
- package/dist/server/storage/index.d.ts +15 -0
- package/dist/server/storage/index.js +27 -0
- package/dist/server/storage/mountMediaRoutes.d.ts +28 -0
- package/dist/server/storage/mountMediaRoutes.js +141 -0
- package/dist/server/storage/objectStoreMediaStore.d.ts +4 -0
- package/dist/server/storage/objectStoreMediaStore.js +57 -0
- package/dist/server/storage/s3ObjectStore.d.ts +63 -0
- package/dist/server/storage/s3ObjectStore.js +187 -0
- package/dist/server/storage/sources.d.ts +47 -0
- package/dist/server/storage/sources.js +148 -0
- package/dist/server/storage/streamRange.d.ts +19 -0
- package/dist/server/storage/streamRange.js +69 -0
- package/dist/server/storage/streamToken.d.ts +16 -0
- package/dist/server/storage/streamToken.js +54 -0
- package/dist/server/storage/transcodeJobs.d.ts +65 -0
- package/dist/server/storage/transcodeJobs.js +104 -0
- package/dist/server/storage/types.d.ts +193 -0
- package/dist/server/storage/types.js +41 -0
- package/dist/server/storage/videoDerivativeKeys.d.ts +127 -0
- package/dist/server/storage/videoDerivativeKeys.js +149 -0
- package/dist/server/storage/videoProcessor.d.ts +30 -0
- package/dist/server/storage/videoProcessor.js +148 -0
- package/dist/server/sync/alarm.d.ts +34 -0
- package/dist/server/sync/alarm.js +91 -0
- package/dist/server/sync/commands.d.ts +87 -0
- package/dist/server/sync/commands.js +241 -0
- package/dist/server/sync/engine.d.ts +62 -0
- package/dist/server/sync/engine.js +184 -0
- package/dist/server/sync/http.d.ts +106 -0
- package/dist/server/sync/http.js +243 -0
- package/dist/server/sync/index.d.ts +41 -0
- package/dist/server/sync/index.js +41 -0
- package/dist/server/sync/opLog.d.ts +61 -0
- package/dist/server/sync/opLog.js +96 -0
- package/dist/server/sync/planner.d.ts +31 -0
- package/dist/server/sync/planner.js +30 -0
- package/dist/server/sync/status.d.ts +63 -0
- package/dist/server/sync/status.js +49 -0
- package/dist/server/sync/timer.d.ts +52 -0
- package/dist/server/sync/timer.js +170 -0
- package/dist/server/sync/tokens.d.ts +25 -0
- package/dist/server/sync/tokens.js +51 -0
- package/dist/server/sync/types.d.ts +101 -0
- package/dist/server/sync/types.js +29 -0
- package/dist/server/ws/eventBus.d.ts +14 -0
- package/dist/server/ws/eventBus.js +42 -0
- package/dist/server/ws/hub.d.ts +20 -0
- package/dist/server/ws/hub.js +154 -0
- package/dist/server/ws/index.d.ts +5 -0
- package/dist/server/ws/index.js +7 -0
- package/dist/server/ws/presence.d.ts +11 -0
- package/dist/server/ws/presence.js +20 -0
- package/dist/server/ws/room.d.ts +17 -0
- package/dist/server/ws/room.js +23 -0
- package/dist/server/ws/types.d.ts +122 -0
- package/dist/server/ws/types.js +1 -0
- package/package.json +234 -0
- package/src/declaredDepsAreImported.spec.ts +87 -0
- package/src/leafSubpathsImportNothing.spec.ts +358 -0
- package/src/lockfileIsTracked.spec.ts +50 -0
- package/src/namedSubpathsResolve.spec.ts +122 -0
- package/src/noPathDeps.spec.ts +24 -0
- package/src/publishShape.spec.ts +89 -0
- package/src/server/activity/index.ts +59 -0
- package/src/server/activity/migrations.ts +67 -0
- package/src/server/activity/shareSource.ts +57 -0
- package/src/server/activity/store.spec.ts +255 -0
- package/src/server/activity/store.ts +368 -0
- package/src/server/app.spec.ts +243 -0
- package/src/server/app.ts +89 -0
- package/src/server/audit/index.ts +2 -0
- package/src/server/audit/migrations.ts +88 -0
- package/src/server/audit/sqliteSink.test.ts +200 -0
- package/src/server/audit/sqliteSink.ts +162 -0
- package/src/server/auth/cookie.spec.ts +46 -0
- package/src/server/auth/cookie.ts +42 -0
- package/src/server/auth/csrf.spec.ts +81 -0
- package/src/server/auth/csrf.ts +83 -0
- package/src/server/auth/fileTokenSigner.ts +40 -0
- package/src/server/auth/getOnly.spec.ts +53 -0
- package/src/server/auth/getOnly.ts +32 -0
- package/src/server/auth/index.ts +51 -0
- package/src/server/auth/jwt.spec.ts +106 -0
- package/src/server/auth/jwt.ts +75 -0
- package/src/server/auth/loginThrottle.spec.ts +213 -0
- package/src/server/auth/loginThrottle.ts +261 -0
- package/src/server/auth/middleware.spec.ts +148 -0
- package/src/server/auth/middleware.ts +56 -0
- package/src/server/auth/password.spec.ts +48 -0
- package/src/server/auth/password.ts +71 -0
- package/src/server/auth/sessionStore.spec.ts +189 -0
- package/src/server/auth/sessionStore.ts +261 -0
- package/src/server/auth/totp.spec.ts +73 -0
- package/src/server/auth/totp.ts +92 -0
- package/src/server/auth/types.ts +146 -0
- package/src/server/auth/vault.spec.ts +51 -0
- package/src/server/auth/vault.ts +71 -0
- package/src/server/context.ts +37 -0
- package/src/server/ctgr.spec.ts +138 -0
- package/src/server/ctgr.ts +108 -0
- package/src/server/db/kysely.spec.ts +62 -0
- package/src/server/db/kysely.ts +21 -0
- package/src/server/db/plumbingTypes.ts +126 -0
- package/src/server/dropzone/duplicates.ts +121 -0
- package/src/server/dropzone/index.ts +30 -0
- package/src/server/dropzone/scan.spec.ts +255 -0
- package/src/server/dropzone/scan.ts +342 -0
- package/src/server/errors.spec.ts +76 -0
- package/src/server/errors.ts +22 -0
- package/src/server/guard/argon2Binding.spec.ts +117 -0
- package/src/server/guard/argon2Binding.ts +195 -0
- package/src/server/guard/argon2Kat.spec.ts +250 -0
- package/src/server/guard/guard.spec.ts +390 -0
- package/src/server/guard/index.ts +6 -0
- package/src/server/guard/kdfWorker.spec.ts +283 -0
- package/src/server/guard/kdfWorker.ts +367 -0
- package/src/server/guard/kdfWorkerEntry.ts +16 -0
- package/src/server/guard/passwordVerifier.spec.ts +71 -0
- package/src/server/guard/passwordVerifier.ts +175 -0
- package/src/server/guard/revocationStore.spec.ts +135 -0
- package/src/server/guard/revocationStore.ts +291 -0
- package/src/server/guard/sessionSigner.ts +425 -0
- package/src/server/guard/verifier.ts +27 -0
- package/src/server/incidents/channels.ts +487 -0
- package/src/server/incidents/checks.ts +120 -0
- package/src/server/incidents/incidents.spec.ts +463 -0
- package/src/server/incidents/index.ts +96 -0
- package/src/server/incidents/migrations.ts +66 -0
- package/src/server/incidents/notifier.ts +215 -0
- package/src/server/incidents/reporter.ts +110 -0
- package/src/server/incidents/routes.ts +314 -0
- package/src/server/incidents/store.ts +329 -0
- package/src/server/incidents/types.ts +116 -0
- package/src/server/index.ts +248 -0
- package/src/server/jobs/fireQueue.spec.ts +85 -0
- package/src/server/jobs/fireQueue.ts +37 -0
- package/src/server/jobs/index.ts +26 -0
- package/src/server/jobs/logger.ts +16 -0
- package/src/server/jobs/plainjobAdapter.spec.ts +34 -0
- package/src/server/jobs/plainjobAdapter.ts +54 -0
- package/src/server/jobs/polling.spec.ts +219 -0
- package/src/server/jobs/polling.ts +81 -0
- package/src/server/jobs/runner.spec.ts +213 -0
- package/src/server/jobs/runner.ts +213 -0
- package/src/server/jobs/timeout.spec.ts +35 -0
- package/src/server/jobs/timeout.ts +53 -0
- package/src/server/jobs/types.ts +188 -0
- package/src/server/master-lock/guard.spec.ts +347 -0
- package/src/server/master-lock/guard.ts +290 -0
- package/src/server/master-lock/index.ts +44 -0
- package/src/server/master-lock/lockPage.spec.ts +88 -0
- package/src/server/master-lock/lockPage.ts +313 -0
- package/src/server/master-lock/masterLock.spec.ts +338 -0
- package/src/server/master-lock/masterLock.ts +627 -0
- package/src/server/master-lock/principals.spec.ts +378 -0
- package/src/server/master-lock/principals.ts +175 -0
- package/src/server/master-lock/seed.spec.ts +69 -0
- package/src/server/master-lock/seed.ts +112 -0
- package/src/server/master-lock/store.ts +35 -0
- package/src/server/media-bun/image.spec.ts +115 -0
- package/src/server/media-bun/image.ts +134 -0
- package/src/server/media-bun/index.ts +52 -0
- package/src/server/media-bun/streaming.spec.ts +134 -0
- package/src/server/media-bun/streaming.ts +347 -0
- package/src/server/media-bun/video.spec.ts +115 -0
- package/src/server/media-bun/video.ts +232 -0
- package/src/server/metrics/metricsBuffer.ts +92 -0
- package/src/server/metrics/normalizeRoute.spec.ts +51 -0
- package/src/server/metrics/normalizeRoute.ts +63 -0
- package/src/server/middleware/correlationId.ts +24 -0
- package/src/server/middleware/corsAllowlist.spec.ts +118 -0
- package/src/server/middleware/corsAllowlist.ts +69 -0
- package/src/server/middleware/errorEnvelope.ts +38 -0
- package/src/server/middleware/index.ts +19 -0
- package/src/server/middleware/localOrigin.spec.ts +168 -0
- package/src/server/middleware/localOrigin.ts +139 -0
- package/src/server/middleware/rateLimit.spec.ts +72 -0
- package/src/server/middleware/rateLimit.ts +125 -0
- package/src/server/middleware/requestLogger.ts +95 -0
- package/src/server/middleware/securityHeaders.ts +78 -0
- package/src/server/middleware/zValidator.spec.ts +251 -0
- package/src/server/middleware/zValidator.ts +114 -0
- package/src/server/migration/runner.spec.ts +88 -0
- package/src/server/migration/runner.ts +87 -0
- package/src/server/migration/types.ts +17 -0
- package/src/server/net/outboundGuard.spec.ts +90 -0
- package/src/server/net/outboundGuard.ts +205 -0
- package/src/server/notifications/hub.ts +245 -0
- package/src/server/notifications/index.ts +77 -0
- package/src/server/notifications/migrations.ts +39 -0
- package/src/server/notifications/routes.ts +177 -0
- package/src/server/notifications/service.spec.ts +305 -0
- package/src/server/notifications/service.ts +112 -0
- package/src/server/paths/appDataHome.spec.ts +87 -0
- package/src/server/paths/appDataHome.ts +84 -0
- package/src/server/private-media/blobRoute.spec.ts +165 -0
- package/src/server/private-media/crypto.spec.ts +81 -0
- package/src/server/private-media/crypto.ts +155 -0
- package/src/server/private-media/gatePolicy.spec.ts +98 -0
- package/src/server/private-media/index-store.ts +83 -0
- package/src/server/private-media/mountPrivateRoutes.ts +265 -0
- package/src/server/private-media/pipeline.spec.ts +111 -0
- package/src/server/private-media/pipeline.ts +170 -0
- package/src/server/private-media/routes.spec.ts +265 -0
- package/src/server/private-media/unlock.ts +112 -0
- package/src/server/resume/index.ts +8 -0
- package/src/server/resume/resume.spec.ts +118 -0
- package/src/server/resume/routes.ts +92 -0
- package/src/server/resume/store.ts +82 -0
- package/src/server/retention/archive.spec.ts +181 -0
- package/src/server/retention/archive.ts +353 -0
- package/src/server/retention/fleet.ts +104 -0
- package/src/server/retention/index.ts +101 -0
- package/src/server/retention/policy.spec.ts +132 -0
- package/src/server/retention/policy.ts +392 -0
- package/src/server/retention/store.spec.ts +236 -0
- package/src/server/retention/store.ts +338 -0
- package/src/server/retention/sweeper.spec.ts +321 -0
- package/src/server/retention/sweeper.ts +367 -0
- package/src/server/retention/target.spec.ts +592 -0
- package/src/server/retention/target.ts +613 -0
- package/src/server/search/index.ts +12 -0
- package/src/server/search/pgSearchProvider.pg.test.ts +392 -0
- package/src/server/search/pgSearchProvider.ts +421 -0
- package/src/server/search/pgSearchSchema.ts +147 -0
- package/src/server/sharing/index.ts +65 -0
- package/src/server/sharing/migrations.ts +128 -0
- package/src/server/sharing/router.spec.ts +167 -0
- package/src/server/sharing/router.ts +194 -0
- package/src/server/sharing/sql.ts +109 -0
- package/src/server/sharing/store.spec.ts +408 -0
- package/src/server/sharing/store.ts +737 -0
- package/src/server/sqlite/backup.spec.ts +114 -0
- package/src/server/sqlite/backup.ts +154 -0
- package/src/server/sqlite/browse.spec.ts +70 -0
- package/src/server/sqlite/browse.ts +120 -0
- package/src/server/sqlite/export.spec.ts +43 -0
- package/src/server/sqlite/export.ts +74 -0
- package/src/server/sqlite/fts5.spec.ts +175 -0
- package/src/server/sqlite/fts5.ts +255 -0
- package/src/server/sqlite/health.spec.ts +79 -0
- package/src/server/sqlite/health.ts +105 -0
- package/src/server/sqlite/import.spec.ts +75 -0
- package/src/server/sqlite/import.ts +141 -0
- package/src/server/sqlite/index.ts +64 -0
- package/src/server/sqlite/readonlySql.spec.ts +58 -0
- package/src/server/sqlite/readonlySql.ts +67 -0
- package/src/server/sqlite/stats.spec.ts +21 -0
- package/src/server/sqlite/stats.ts +61 -0
- package/src/server/sqlite/viewer.spec.ts +127 -0
- package/src/server/sqlite/viewer.ts +72 -0
- package/src/server/storage/binaryServerProcessing.integration.spec.ts +222 -0
- package/src/server/storage/binaryServerProcessing.spec.ts +150 -0
- package/src/server/storage/binaryServerProcessing.ts +127 -0
- package/src/server/storage/binaryServerStore.ts +102 -0
- package/src/server/storage/chunked.integration.spec.ts +185 -0
- package/src/server/storage/diskStore.ts +83 -0
- package/src/server/storage/files/adapter.photo.display.spec.ts +169 -0
- package/src/server/storage/files/adapter.photo.spec.ts +128 -0
- package/src/server/storage/files/adapter.spec.ts +121 -0
- package/src/server/storage/files/adapter.ts +255 -0
- package/src/server/storage/files/catalogue.media-host.spec.ts +184 -0
- package/src/server/storage/files/catalogue.orderings.spec.ts +157 -0
- package/src/server/storage/files/catalogue.private.spec.ts +106 -0
- package/src/server/storage/files/catalogue.recover.spec.ts +131 -0
- package/src/server/storage/files/catalogue.sort.spec.ts +154 -0
- package/src/server/storage/files/catalogue.spec.ts +133 -0
- package/src/server/storage/files/catalogue.trash-import.spec.ts +125 -0
- package/src/server/storage/files/catalogue.ts +1326 -0
- package/src/server/storage/files/chunkedUpload.spec.ts +111 -0
- package/src/server/storage/files/chunkedUpload.ts +146 -0
- package/src/server/storage/files/collections.spec.ts +294 -0
- package/src/server/storage/files/fileRoutes.replace.spec.ts +88 -0
- package/src/server/storage/files/fileRoutes.spec.ts +327 -0
- package/src/server/storage/files/fileRoutes.ts +462 -0
- package/src/server/storage/files/index.ts +66 -0
- package/src/server/storage/files/migrations.ts +187 -0
- package/src/server/storage/files/mimeByExt.ts +14 -0
- package/src/server/storage/files/stack.spec.ts +258 -0
- package/src/server/storage/files/stack.ts +284 -0
- package/src/server/storage/files/storageOps.spec.ts +126 -0
- package/src/server/storage/files/storageOps.ts +213 -0
- package/src/server/storage/files/types.ts +285 -0
- package/src/server/storage/imageThumbnail.spec.ts +81 -0
- package/src/server/storage/imageThumbnail.ts +60 -0
- package/src/server/storage/index.ts +122 -0
- package/src/server/storage/mountMediaRoutes.ts +231 -0
- package/src/server/storage/objectStore.spec.ts +272 -0
- package/src/server/storage/objectStoreMediaStore.ts +63 -0
- package/src/server/storage/s3ObjectStore.ts +239 -0
- package/src/server/storage/sources.ts +245 -0
- package/src/server/storage/storage.spec.ts +219 -0
- package/src/server/storage/streamRange.ts +92 -0
- package/src/server/storage/streamToken.ts +76 -0
- package/src/server/storage/transcode.integration.spec.ts +134 -0
- package/src/server/storage/transcodeJobs.spec.ts +152 -0
- package/src/server/storage/transcodeJobs.ts +171 -0
- package/src/server/storage/types.ts +230 -0
- package/src/server/storage/videoDerivativeKeys.spec.ts +112 -0
- package/src/server/storage/videoDerivativeKeys.ts +172 -0
- package/src/server/storage/videoProcessor.posterOnly.spec.ts +106 -0
- package/src/server/storage/videoProcessor.ts +229 -0
- package/src/server/sync/alarm.spec.ts +149 -0
- package/src/server/sync/alarm.ts +137 -0
- package/src/server/sync/commands.spec.ts +145 -0
- package/src/server/sync/commands.ts +361 -0
- package/src/server/sync/engine.spec.ts +496 -0
- package/src/server/sync/engine.ts +255 -0
- package/src/server/sync/http.ts +316 -0
- package/src/server/sync/httpRemote.spec.ts +158 -0
- package/src/server/sync/index.ts +90 -0
- package/src/server/sync/opLog.spec.ts +110 -0
- package/src/server/sync/opLog.ts +189 -0
- package/src/server/sync/planner.spec.ts +53 -0
- package/src/server/sync/planner.ts +62 -0
- package/src/server/sync/status.spec.ts +92 -0
- package/src/server/sync/status.ts +108 -0
- package/src/server/sync/timer.spec.ts +150 -0
- package/src/server/sync/timer.ts +207 -0
- package/src/server/sync/tokens.spec.ts +38 -0
- package/src/server/sync/tokens.ts +94 -0
- package/src/server/sync/types.ts +108 -0
- package/src/server/ws/eventBus.spec.ts +67 -0
- package/src/server/ws/eventBus.ts +46 -0
- package/src/server/ws/hub.integration.spec.ts +93 -0
- package/src/server/ws/hub.spec.ts +282 -0
- package/src/server/ws/hub.ts +168 -0
- package/src/server/ws/index.ts +20 -0
- package/src/server/ws/presence.spec.ts +27 -0
- package/src/server/ws/presence.ts +26 -0
- package/src/server/ws/room.spec.ts +36 -0
- package/src/server/ws/room.ts +29 -0
- package/src/server/ws/types.ts +134 -0
- package/src/skippedTestsAreAnswered.spec.ts +215 -0
- package/src/testTmpRoot.spec.ts +102 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { Database } from 'bun:sqlite';
|
|
2
|
+
/**
|
|
3
|
+
* The cursedbelt job runner's public types — the engine both apps share.
|
|
4
|
+
*
|
|
5
|
+
* cursedbelt owns the *mechanism* (durable cron scheduling, restart-surviving
|
|
6
|
+
* pollers, the due-row fire queue, run auditing); the *job definitions* —
|
|
7
|
+
* reminder delivery, recurrence materialization, backup, metrics flush, caSync —
|
|
8
|
+
* stay app-business and consume these primitives.
|
|
9
|
+
*
|
|
10
|
+
* Three primitives cover every in-process scheduled-work shape:
|
|
11
|
+
* - {@link JobRunner} — durable cron jobs (plainjob-backed; survive restart).
|
|
12
|
+
* - PollingJob — a periodic timer that records its last successful run.
|
|
13
|
+
* - FireQueue — the "select due rows → deliver → mark sent" pattern.
|
|
14
|
+
*
|
|
15
|
+
* (cwip/taskq is the *other* model — lease-locked, multi-process, cross-machine —
|
|
16
|
+
* and stays in cwip. These primitives are single-server / in-process only.)
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* A minimal structured logger (winston-compatible, so it can be passed straight
|
|
20
|
+
* through to the underlying plainjob queue/worker). Consumers inject their app
|
|
21
|
+
* logger; the default ({@link defaultJobLogger}) writes warn/info/error to the
|
|
22
|
+
* console and silences `debug` (plainjob logs at debug every poll).
|
|
23
|
+
*/
|
|
24
|
+
export interface JobLogger {
|
|
25
|
+
error(message: string, ...meta: unknown[]): void;
|
|
26
|
+
warn(message: string, ...meta: unknown[]): void;
|
|
27
|
+
info(message: string, ...meta: unknown[]): void;
|
|
28
|
+
debug(message: string, ...meta: unknown[]): void;
|
|
29
|
+
}
|
|
30
|
+
/** Everything a job handler is handed when it runs. */
|
|
31
|
+
export interface JobContext {
|
|
32
|
+
/** The shared SQLite connection (same instance the app uses). */
|
|
33
|
+
db: Database;
|
|
34
|
+
/**
|
|
35
|
+
* Aborted when the runner/poller is stopped, or when a {@link JobDef.timeout}
|
|
36
|
+
* elapses. Long-running handlers should observe it and short-circuit; a promise
|
|
37
|
+
* cannot be force-killed, so cooperation is the only way to stop work early.
|
|
38
|
+
*/
|
|
39
|
+
signal: AbortSignal;
|
|
40
|
+
logger: JobLogger;
|
|
41
|
+
}
|
|
42
|
+
/** A durable, cron-scheduled job registered with a {@link JobRunner}. */
|
|
43
|
+
export interface JobDef {
|
|
44
|
+
/** Unique name — the audit key (`job_runs.job_name`) and plainjob job type. */
|
|
45
|
+
name: string;
|
|
46
|
+
/**
|
|
47
|
+
* Cron expression. 5-field (minute precision) or 6-field with a leading
|
|
48
|
+
* seconds column — e.g. `0 3 * * *` is 3am daily, and a seconds step of the
|
|
49
|
+
* form `(asterisk-slash-10) * * * * *` fires every 10s. Omit for a job that is
|
|
50
|
+
* only ever fired manually via {@link JobRunner.runOnce}.
|
|
51
|
+
*/
|
|
52
|
+
cron?: string;
|
|
53
|
+
/** The work. Rejecting (or timing out) records an `error` run. */
|
|
54
|
+
run(context: JobContext): Promise<void> | void;
|
|
55
|
+
/**
|
|
56
|
+
* Max run time in ms before the context signal is aborted and the run is
|
|
57
|
+
* recorded as a timeout error. Default {@link DEFAULT_JOB_TIMEOUT_MS}. Set a
|
|
58
|
+
* larger value (or `0` to disable) for legitimately long jobs like backups.
|
|
59
|
+
*/
|
|
60
|
+
timeout?: number;
|
|
61
|
+
}
|
|
62
|
+
/** A single row of a job's run history (newest first via {@link JobRunner.getLastRun}). */
|
|
63
|
+
export interface JobRunRecord {
|
|
64
|
+
startedAt: string;
|
|
65
|
+
/** Null while the run is still in flight (or if the process died mid-run). */
|
|
66
|
+
endedAt?: string;
|
|
67
|
+
/** `running` | `ok` | `error`. */
|
|
68
|
+
status: string;
|
|
69
|
+
/** Present only on an `error` run. */
|
|
70
|
+
error?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface CreateJobRunnerOptions {
|
|
73
|
+
logger?: JobLogger;
|
|
74
|
+
/**
|
|
75
|
+
* How often each worker polls for due cron jobs, in ms. Default 1000. Set lower
|
|
76
|
+
* only if you need sub-second cron precision; the cost is one cheap SQLite query
|
|
77
|
+
* per worker per interval.
|
|
78
|
+
*/
|
|
79
|
+
pollIntervalMs?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Bounds on the runner's persisted history. Without bounds a high-frequency
|
|
82
|
+
* cron (e.g. a 5s engine janitor) grows `job_runs` and plainjob's internal
|
|
83
|
+
* `plainjob_jobs` forever — thousands of rows per day on a completely idle
|
|
84
|
+
* daemon. Retention is ON by default ({@link DEFAULT_JOB_RETENTION});
|
|
85
|
+
* override a field to tune it, or set a field to `0` to disable that bound.
|
|
86
|
+
*/
|
|
87
|
+
retention?: JobRetentionOptions;
|
|
88
|
+
}
|
|
89
|
+
/** Retention bounds for {@link JobRunner} history. `0` disables that bound. */
|
|
90
|
+
export interface JobRetentionOptions {
|
|
91
|
+
/** Delete `job_runs` audit rows older than this. Default 7 days. */
|
|
92
|
+
auditMaxAgeMs?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Keep at most this many newest `job_runs` rows PER job name — the bound that
|
|
95
|
+
* matters for sub-minute crons, where an age window alone still admits tens of
|
|
96
|
+
* thousands of rows. Default 2000.
|
|
97
|
+
*/
|
|
98
|
+
auditMaxRowsPerJob?: number;
|
|
99
|
+
/** Minimum gap between prune passes (pruning is lazy, after a run finishes). Default 1 hour. */
|
|
100
|
+
pruneIntervalMs?: number;
|
|
101
|
+
/** plainjob maintenance: remove its internal done-job rows older than this. Default 6 hours. */
|
|
102
|
+
doneJobsMaxAgeMs?: number;
|
|
103
|
+
/** plainjob maintenance: remove its internal failed-job rows older than this. Default 7 days. */
|
|
104
|
+
failedJobsMaxAgeMs?: number;
|
|
105
|
+
}
|
|
106
|
+
/** Default {@link JobRetentionOptions} — bounded history out of the box. */
|
|
107
|
+
export declare const DEFAULT_JOB_RETENTION: Required<JobRetentionOptions>;
|
|
108
|
+
/**
|
|
109
|
+
* The durable cron job runner — a thin facade over plainjob (SQLite-backed, no
|
|
110
|
+
* Redis). Schedules persist in the shared DB, so a registered cron survives a
|
|
111
|
+
* restart. (plainjob does NOT retroactively fire a cron missed while the process
|
|
112
|
+
* was down; for "must catch up on boot" work use a PollingJob with `runOnBoot`.)
|
|
113
|
+
*/
|
|
114
|
+
export interface JobRunner {
|
|
115
|
+
/** Register a job. Call before {@link start}; registering a cron after start spins its worker up immediately. */
|
|
116
|
+
register(job: JobDef): void;
|
|
117
|
+
/** Begin processing: one worker per cron job starts polling. Idempotent. */
|
|
118
|
+
start(): void;
|
|
119
|
+
/** Stop all workers (signaling in-flight handlers) and release the queue. Does NOT close the shared DB. */
|
|
120
|
+
stop(): Promise<void>;
|
|
121
|
+
/** Run a registered job once, right now (admin/manual trigger). Bypasses the schedule; still audited. Throws ApiError(404) if unknown. */
|
|
122
|
+
runOnce(name: string): Promise<void>;
|
|
123
|
+
/** The most recent run of `name`, or null if it has never run. */
|
|
124
|
+
getLastRun(name: string): JobRunRecord | null;
|
|
125
|
+
}
|
|
126
|
+
/** A periodic timer started/stopped as a unit. Returned by `createPollingJob`/`createFireQueue`. */
|
|
127
|
+
export interface PollingJob {
|
|
128
|
+
name: string;
|
|
129
|
+
/** Start the timer (and fire once immediately unless `runOnBoot` is false). Idempotent. */
|
|
130
|
+
start(): void;
|
|
131
|
+
/** Stop the timer and signal any in-flight run via its abort signal. */
|
|
132
|
+
stop(): void;
|
|
133
|
+
}
|
|
134
|
+
export interface PollingJobOpts {
|
|
135
|
+
name: string;
|
|
136
|
+
/** Fire cadence in ms. */
|
|
137
|
+
intervalMs: number;
|
|
138
|
+
/** Fire once immediately on {@link PollingJob.start}. Default true. */
|
|
139
|
+
runOnBoot?: boolean;
|
|
140
|
+
/** The shared SQLite connection. `job_state.last_run_at` is upserted after each successful run. */
|
|
141
|
+
db: Database;
|
|
142
|
+
run(context: JobContext): Promise<void> | void;
|
|
143
|
+
logger?: JobLogger;
|
|
144
|
+
}
|
|
145
|
+
/** A {@link PollingJob} specialized for the "deliver every due row" pattern. */
|
|
146
|
+
export type FireQueue = PollingJob;
|
|
147
|
+
export interface FireQueueOpts<T> {
|
|
148
|
+
name: string;
|
|
149
|
+
db: Database;
|
|
150
|
+
/** Poll cadence in ms. Default {@link DEFAULT_FIRE_INTERVAL_MS} (30s). */
|
|
151
|
+
intervalMs?: number;
|
|
152
|
+
/** Fire once immediately on start. Default true. */
|
|
153
|
+
runOnBoot?: boolean;
|
|
154
|
+
/** Return the rows currently due for delivery. */
|
|
155
|
+
getDue(db: Database): T[];
|
|
156
|
+
/** Deliver one item. Throwing skips `markSent` for that item and routes to `onError`; the loop continues. */
|
|
157
|
+
deliver(item: T, db: Database): Promise<void> | void;
|
|
158
|
+
/** Mark one item delivered (only called after a successful `deliver`). */
|
|
159
|
+
markSent(item: T, db: Database): void;
|
|
160
|
+
/** Per-item failure hook (the loop still continues to the next item). */
|
|
161
|
+
onError?(item: T, err: unknown): void;
|
|
162
|
+
logger?: JobLogger;
|
|
163
|
+
}
|
|
164
|
+
/** Default per-run timeout for a cron {@link JobDef} (1 minute). */
|
|
165
|
+
export declare const DEFAULT_JOB_TIMEOUT_MS = 60000;
|
|
166
|
+
/** Default poll cadence for a {@link FireQueue} (30 seconds). */
|
|
167
|
+
export declare const DEFAULT_FIRE_INTERVAL_MS = 30000;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Default {@link JobRetentionOptions} — bounded history out of the box. */
|
|
2
|
+
export const DEFAULT_JOB_RETENTION = {
|
|
3
|
+
auditMaxAgeMs: 7 * 24 * 60 * 60 * 1000,
|
|
4
|
+
auditMaxRowsPerJob: 2000,
|
|
5
|
+
pruneIntervalMs: 60 * 60 * 1000,
|
|
6
|
+
doneJobsMaxAgeMs: 6 * 60 * 60 * 1000,
|
|
7
|
+
failedJobsMaxAgeMs: 7 * 24 * 60 * 60 * 1000,
|
|
8
|
+
};
|
|
9
|
+
/** Default per-run timeout for a cron {@link JobDef} (1 minute). */
|
|
10
|
+
export const DEFAULT_JOB_TIMEOUT_MS = 60_000;
|
|
11
|
+
/** Default poll cadence for a {@link FireQueue} (30 seconds). */
|
|
12
|
+
export const DEFAULT_FIRE_INTERVAL_MS = 30_000;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type LockPageOptions } from "./lockPage";
|
|
2
|
+
import type { MasterLock } from "./masterLock";
|
|
3
|
+
export interface MasterLockGuardOptions extends LockPageOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The lock this guard enforces.
|
|
6
|
+
*
|
|
7
|
+
* A {@link MasterLock} for an app with one app-wide password (collections, the
|
|
8
|
+
* binary-server inspector). A FUNCTION for an app where each person has their own
|
|
9
|
+
* (`principals.ts`): it is handed the request and returns that caller's lock.
|
|
10
|
+
*
|
|
11
|
+
* 🔴 The function must resolve the principal from the request's own verified session
|
|
12
|
+
* and NOTHING else. A resolver that reads a header, a query parameter or a body field
|
|
13
|
+
* hands the caller the choice of whose lock to be measured against, which is the one
|
|
14
|
+
* way this feature can be turned inside out. Returning `null` means "no principal" —
|
|
15
|
+
* the guard then stands aside for ordinary paths (the app's own sign-in owns the
|
|
16
|
+
* request) and refuses `/__lock/*` outright, since there is nobody to unlock for.
|
|
17
|
+
*/
|
|
18
|
+
lock: MasterLock | ((req: Request) => MasterLock | null);
|
|
19
|
+
/**
|
|
20
|
+
* Paths that stay reachable while locked, beyond `/__lock/*`.
|
|
21
|
+
*
|
|
22
|
+
* 🔴 This is where a MACHINE surface is kept out of the lock, and getting it wrong is
|
|
23
|
+
* the one way this feature can break something else. binary-server's inspector is a
|
|
24
|
+
* UI and is locked whole; binary-server's STORE serves the fleet's bytes and is a
|
|
25
|
+
* different process that never mounts this at all. Within one process, a health check
|
|
26
|
+
* or a token-authenticated ingest route belongs here — a page does not.
|
|
27
|
+
*/
|
|
28
|
+
allow?: (url: URL, req: Request) => boolean;
|
|
29
|
+
/**
|
|
30
|
+
* What makes this app's unlock cookie ITS OWN — `master_lock_<appKey>`.
|
|
31
|
+
*
|
|
32
|
+
* 🔴 Set it on every app that can ever share a hostname with another locked app, which
|
|
33
|
+
* on this fleet means every app: they are all `localhost:<port>` on the owner's Mac, and
|
|
34
|
+
* a cookie is scoped by HOST, not by origin. Left unset the cookie is the bare
|
|
35
|
+
* `master_lock`, and two such apps overwrite each other's token — see
|
|
36
|
+
* `masterLockCookieName` for what that silently breaks. Defaults to {@link appLabel},
|
|
37
|
+
* so an app that already names itself is fixed without being changed.
|
|
38
|
+
*/
|
|
39
|
+
appKey?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Is this connection HTTPS, so the cookie may carry `Secure`?
|
|
42
|
+
*
|
|
43
|
+
* Defaults to reading `x-forwarded-proto` then the URL — right for both a satellite
|
|
44
|
+
* behind nginx and a loopback daemon speaking plain http, where a `Secure` cookie would
|
|
45
|
+
* simply never be stored and the site could never be unlocked.
|
|
46
|
+
*/
|
|
47
|
+
secure?: (url: URL, req: Request) => boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface MasterLockGuard {
|
|
50
|
+
/** `Response` when handled, `null` when the app should serve the request. */
|
|
51
|
+
handle(req: Request): Promise<Response | null>;
|
|
52
|
+
}
|
|
53
|
+
export declare function createMasterLockGuard(options: MasterLockGuardOptions): MasterLockGuard;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guard — one decision function, so a Hono app and a bare `Bun.serve` handler cannot
|
|
3
|
+
* end up enforcing two different rules.
|
|
4
|
+
*
|
|
5
|
+
* `handle(req)` answers a `Response` when it has taken responsibility for the request, and
|
|
6
|
+
* `null` when the app should serve it as usual. That shape is what lets it be middleware in
|
|
7
|
+
* one app and an early `return` in another without either of them re-deciding anything.
|
|
8
|
+
*
|
|
9
|
+
* 🔴 Registration order is the security property, exactly as it is for the SSO gate this
|
|
10
|
+
* sits behind. Mount it so that it runs BEFORE any route that serves content, and AFTER
|
|
11
|
+
* whatever proves the caller is allowed in the building at all — the master password is a
|
|
12
|
+
* second wall, not a replacement for the first.
|
|
13
|
+
*/
|
|
14
|
+
import { MASTER_LOCK_PATHS, MASTER_LOCK_PREFIX, MASTER_LOCK_STATE_HEADER, masterLockCookieName, } from "cursedbelt-core/master-lock";
|
|
15
|
+
import { LOCK_SCRIPT, LOCK_STYLE, lockPageHtml } from "./lockPage";
|
|
16
|
+
const noStore = { "cache-control": "no-store, no-cache, must-revalidate", pragma: "no-cache" };
|
|
17
|
+
/**
|
|
18
|
+
* The lock page's own CSP. Strict enough to stand in front of `apps/vault`'s wall without
|
|
19
|
+
* loosening it: nothing loads from anywhere but this origin, the form posts nowhere (the
|
|
20
|
+
* script does the POST), and the page cannot be framed.
|
|
21
|
+
*/
|
|
22
|
+
const LOCK_CSP = "default-src 'none'; style-src 'self'; script-src 'self'; connect-src 'self'; " +
|
|
23
|
+
"img-src data:; form-action 'none'; base-uri 'none'; frame-ancestors 'none'";
|
|
24
|
+
export function createMasterLockGuard(options) {
|
|
25
|
+
const resolve = typeof options.lock === "function" ? options.lock : () => options.lock;
|
|
26
|
+
const isSecure = options.secure ?? defaultSecure;
|
|
27
|
+
const cookieName = masterLockCookieName(options.appKey ?? options.appLabel);
|
|
28
|
+
const cookie = (token, secure) =>
|
|
29
|
+
// No Max-Age: a SESSION cookie, so closing the browser drops it. The server-side
|
|
30
|
+
// idle clock is the real expiry — this only keeps the token out of a profile that
|
|
31
|
+
// somebody else might open tomorrow.
|
|
32
|
+
`${cookieName}=${token}; Path=/; HttpOnly; SameSite=Strict${secure ? "; Secure" : ""}`;
|
|
33
|
+
const clearCookie = (secure) => `${cookieName}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0${secure ? "; Secure" : ""}`;
|
|
34
|
+
/** The ONE refusal. Identical for wrong, throttled, unconfigured and not-unlocked. */
|
|
35
|
+
const refuse = (retryAfterMs = 0) => Response.json({ error: "locked", retryAfterMs }, { status: 401, headers: { ...noStore, [MASTER_LOCK_STATE_HEADER]: "locked" } });
|
|
36
|
+
/**
|
|
37
|
+
* 🔴 May THIS request through — and the difference between the two modes is the whole
|
|
38
|
+
* security property of the per-person wall.
|
|
39
|
+
*
|
|
40
|
+
* **App-wide**: `unlocked` is enough. The site has one user and one unlock; the token
|
|
41
|
+
* is how a caller with no cookie jar says "it's still me", not a per-browser
|
|
42
|
+
* credential. Reading the ambient state is the behaviour `apps/collections` and the
|
|
43
|
+
* binary-server inspector have always had.
|
|
44
|
+
*
|
|
45
|
+
* **Per-person**: the token is REQUIRED. `unlocked` there means only "somebody's
|
|
46
|
+
* device is open", and honouring that would make the unlock ambient — measured while
|
|
47
|
+
* building this (2026-08-25): with `unlocked ||` in front, one browser unlocking meant
|
|
48
|
+
* every OTHER browser signed in as that person walked straight through, having typed
|
|
49
|
+
* nothing. The owner's phone would ride his desk's unlock, and an invented
|
|
50
|
+
* `x-master-lock` value would be accepted because the check never looked at it. Two
|
|
51
|
+
* tests in `principals.spec.ts` fail on the `||`, which is why it is gone.
|
|
52
|
+
*/
|
|
53
|
+
const opens = (lock, req) => lock.isEnrollable ? lock.presents(req) : lock.unlocked || lock.presents(req);
|
|
54
|
+
async function handleOwnRoute(req, url, lock) {
|
|
55
|
+
const path = url.pathname;
|
|
56
|
+
const secure = isSecure(url, req);
|
|
57
|
+
if (req.method === "GET" || req.method === "HEAD") {
|
|
58
|
+
if (path === MASTER_LOCK_PATHS.style) {
|
|
59
|
+
return new Response(LOCK_STYLE, {
|
|
60
|
+
headers: { "content-type": "text/css; charset=utf-8", ...noStore },
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (path === MASTER_LOCK_PATHS.script) {
|
|
64
|
+
return new Response(LOCK_SCRIPT, {
|
|
65
|
+
headers: { "content-type": "text/javascript; charset=utf-8", ...noStore },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (path === MASTER_LOCK_PATHS.status) {
|
|
69
|
+
return Response.json(lock.status(), { headers: noStore });
|
|
70
|
+
}
|
|
71
|
+
if (path === MASTER_LOCK_PATHS.page || path === MASTER_LOCK_PREFIX) {
|
|
72
|
+
// Already open FOR THIS CALLER, so there is nothing to type — send them to
|
|
73
|
+
// the app rather than showing a password box that would do nothing. Per
|
|
74
|
+
// person this must be the caller's own state, not the site's: bouncing a
|
|
75
|
+
// locked-out phone to `/` because the desk is open is an infinite loop.
|
|
76
|
+
if (opens(lock, req))
|
|
77
|
+
return Response.redirect(new URL("/", url), 303);
|
|
78
|
+
return lockPage(options, lock);
|
|
79
|
+
}
|
|
80
|
+
return new Response("not found", { status: 404, headers: noStore });
|
|
81
|
+
}
|
|
82
|
+
if (req.method !== "POST") {
|
|
83
|
+
return new Response("method not allowed", { status: 405, headers: noStore });
|
|
84
|
+
}
|
|
85
|
+
if (path === MASTER_LOCK_PATHS.enroll) {
|
|
86
|
+
const body = await readJson(req);
|
|
87
|
+
const result = await lock.enroll({
|
|
88
|
+
kdf: body?.kdf,
|
|
89
|
+
verifier: typeof body?.verifier === "string" ? body.verifier : "",
|
|
90
|
+
});
|
|
91
|
+
// Every refusal is the SAME refusal, as everywhere else here: "you may not
|
|
92
|
+
// enroll" and "somebody already did" must not be distinguishable, or the route
|
|
93
|
+
// becomes a way to ask whether a given account has a master password yet.
|
|
94
|
+
if (!result.ok)
|
|
95
|
+
return refuse();
|
|
96
|
+
return Response.json({ ok: true, idleMs: lock.idleMs }, { headers: { ...noStore, "set-cookie": cookie(result.token, secure) } });
|
|
97
|
+
}
|
|
98
|
+
if (path === MASTER_LOCK_PATHS.unlock) {
|
|
99
|
+
const body = await readJson(req);
|
|
100
|
+
const verifier = typeof body?.verifier === "string" ? body.verifier : "";
|
|
101
|
+
const result = await lock.unlock(verifier);
|
|
102
|
+
if (!result.ok)
|
|
103
|
+
return refuse(result.retryAfterMs);
|
|
104
|
+
return Response.json({ ok: true, idleMs: lock.idleMs }, { headers: { ...noStore, "set-cookie": cookie(result.token, secure) } });
|
|
105
|
+
}
|
|
106
|
+
// Everything below CHANGES something and therefore needs the site already open.
|
|
107
|
+
if (!lock.presents(req))
|
|
108
|
+
return refuse();
|
|
109
|
+
if (path === MASTER_LOCK_PATHS.ping) {
|
|
110
|
+
lock.touch(req);
|
|
111
|
+
return Response.json({ ok: true, remainingMs: lock.remainingMs }, { headers: noStore });
|
|
112
|
+
}
|
|
113
|
+
if (path === MASTER_LOCK_PATHS.lock) {
|
|
114
|
+
lock.lock();
|
|
115
|
+
return Response.json({ ok: true }, { headers: { ...noStore, "set-cookie": clearCookie(secure) } });
|
|
116
|
+
}
|
|
117
|
+
if (path === MASTER_LOCK_PATHS.settings) {
|
|
118
|
+
const body = await readJson(req);
|
|
119
|
+
const idleMs = lock.setIdleMs(Number(body?.idleMs));
|
|
120
|
+
return Response.json({ ok: true, idleMs }, { headers: noStore });
|
|
121
|
+
}
|
|
122
|
+
if (path === MASTER_LOCK_PATHS.change) {
|
|
123
|
+
const body = await readJson(req);
|
|
124
|
+
const result = await lock.change({
|
|
125
|
+
currentVerifier: typeof body?.currentVerifier === "string" ? body.currentVerifier : "",
|
|
126
|
+
kdf: body?.kdf,
|
|
127
|
+
verifier: typeof body?.verifier === "string" ? body.verifier : "",
|
|
128
|
+
});
|
|
129
|
+
if (!result.ok) {
|
|
130
|
+
return Response.json({ error: result.reason }, { status: 400, headers: noStore });
|
|
131
|
+
}
|
|
132
|
+
// The rotation locked the site; drop the cookie with it so the page it came from
|
|
133
|
+
// does not sit there holding a token that stopped meaning anything.
|
|
134
|
+
return Response.json({ ok: true }, { headers: { ...noStore, "set-cookie": clearCookie(secure) } });
|
|
135
|
+
}
|
|
136
|
+
return new Response("not found", { status: 404, headers: noStore });
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
async handle(req) {
|
|
140
|
+
const url = new URL(req.url);
|
|
141
|
+
const own = url.pathname === MASTER_LOCK_PREFIX || url.pathname.startsWith(`${MASTER_LOCK_PREFIX}/`);
|
|
142
|
+
const lock = resolve(req);
|
|
143
|
+
if (!lock) {
|
|
144
|
+
// No principal. On the app's own paths the caller is not signed in and the
|
|
145
|
+
// app's sign-in owns the request; on `/__lock/*` there is nobody to unlock
|
|
146
|
+
// FOR, and answering the ordinary refusal keeps that indistinguishable from
|
|
147
|
+
// being locked.
|
|
148
|
+
return own ? refuse() : null;
|
|
149
|
+
}
|
|
150
|
+
if (own)
|
|
151
|
+
return handleOwnRoute(req, url, lock);
|
|
152
|
+
if (options.allow?.(url, req))
|
|
153
|
+
return null;
|
|
154
|
+
if (opens(lock, req))
|
|
155
|
+
return null;
|
|
156
|
+
// A document request is answered IN PLACE with the lock page — same URL, status
|
|
157
|
+
// 200 — so unlocking reloads to where they were going instead of dumping them at
|
|
158
|
+
// the root. Everything else gets the uniform refusal.
|
|
159
|
+
if (wantsDocument(req))
|
|
160
|
+
return lockPage(options, lock);
|
|
161
|
+
return refuse();
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function lockPage(options, lock) {
|
|
166
|
+
return new Response(lockPageHtml({ ...options, enroll: lock.awaitingEnrollment }), {
|
|
167
|
+
status: 200,
|
|
168
|
+
headers: {
|
|
169
|
+
"content-type": "text/html; charset=utf-8",
|
|
170
|
+
"content-security-policy": LOCK_CSP,
|
|
171
|
+
"x-content-type-options": "nosniff",
|
|
172
|
+
[MASTER_LOCK_STATE_HEADER]: "locked",
|
|
173
|
+
...noStore,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/** Is this the browser asking for a PAGE, as opposed to the app asking for data? */
|
|
178
|
+
function wantsDocument(req) {
|
|
179
|
+
if (req.method !== "GET")
|
|
180
|
+
return false;
|
|
181
|
+
// `Sec-Fetch-Dest` is the browser's own answer and beats guessing from `Accept`; every
|
|
182
|
+
// engine this fleet runs on sends it. `Accept` is the fallback for curl and for a
|
|
183
|
+
// request that arrived through something that stripped it.
|
|
184
|
+
const dest = req.headers.get("sec-fetch-dest");
|
|
185
|
+
if (dest)
|
|
186
|
+
return dest === "document" || dest === "iframe";
|
|
187
|
+
return (req.headers.get("accept") ?? "").includes("text/html");
|
|
188
|
+
}
|
|
189
|
+
async function readJson(req) {
|
|
190
|
+
try {
|
|
191
|
+
const body = await req.json();
|
|
192
|
+
if (body === null || typeof body !== "object" || Array.isArray(body))
|
|
193
|
+
return null;
|
|
194
|
+
return body;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function defaultSecure(url, req) {
|
|
201
|
+
const forwarded = req.headers.get("x-forwarded-proto");
|
|
202
|
+
if (forwarded)
|
|
203
|
+
return forwarded.split(",")[0]?.trim() === "https";
|
|
204
|
+
return url.protocol === "https:";
|
|
205
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cursedbelt-server/master-lock` — the server half of the sitewide master-password lock.
|
|
3
|
+
*
|
|
4
|
+
* What the feature is, what it deliberately is NOT, and the four decisions behind it are in
|
|
5
|
+
* `masterLock.ts`'s module note. Read that first.
|
|
6
|
+
*
|
|
7
|
+
* Adopting it in an app is three things:
|
|
8
|
+
* 1. a {@link MasterLock} built over whatever key/value the app already has,
|
|
9
|
+
* 2. `createMasterLockGuard(...).handle(req)` in front of everything the app serves,
|
|
10
|
+
* 3. `cursedbelt/react/master-lock` in the header, for the manual lock and the rotation.
|
|
11
|
+
*
|
|
12
|
+
* Removing it is deleting those three. That is the shape the owner asked for: *"the reusable
|
|
13
|
+
* functionality for this should be in a place that makes it easy to share to add or remove
|
|
14
|
+
* from apps in the code, such as cb."*
|
|
15
|
+
*/
|
|
16
|
+
export { type MasterLockGuard, type MasterLockGuardOptions, createMasterLockGuard } from "./guard";
|
|
17
|
+
export { LOCK_SCRIPT, LOCK_STYLE, type LockPageOptions, lockPageHtml } from "./lockPage";
|
|
18
|
+
export { MASTER_LOCK_PRINCIPAL_KEY_PREFIX, MasterLockDirectory, type MasterLockDirectoryOptions, masterLockPrincipalKey, } from "./principals";
|
|
19
|
+
export { MasterLock, type MasterLockEnrollResult, type MasterLockOptions, type MasterLockRecord, type MasterLockStore, type MasterLockUnlockResult, readRecord, } from "./masterLock";
|
|
20
|
+
export { DEFAULT_SEED_IDLE_MS, generateStagePassword, type MintMasterLockSeedOptions, mintMasterLockSeed, serializeMasterLockSeed, } from "./seed";
|
|
21
|
+
export { MASTER_LOCK_SETTING_KEY, createKvMasterLockStore, createMemoryMasterLockStore, } from "./store";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cursedbelt-server/master-lock` — the server half of the sitewide master-password lock.
|
|
3
|
+
*
|
|
4
|
+
* What the feature is, what it deliberately is NOT, and the four decisions behind it are in
|
|
5
|
+
* `masterLock.ts`'s module note. Read that first.
|
|
6
|
+
*
|
|
7
|
+
* Adopting it in an app is three things:
|
|
8
|
+
* 1. a {@link MasterLock} built over whatever key/value the app already has,
|
|
9
|
+
* 2. `createMasterLockGuard(...).handle(req)` in front of everything the app serves,
|
|
10
|
+
* 3. `cursedbelt/react/master-lock` in the header, for the manual lock and the rotation.
|
|
11
|
+
*
|
|
12
|
+
* Removing it is deleting those three. That is the shape the owner asked for: *"the reusable
|
|
13
|
+
* functionality for this should be in a place that makes it easy to share to add or remove
|
|
14
|
+
* from apps in the code, such as cb."*
|
|
15
|
+
*/
|
|
16
|
+
export { createMasterLockGuard } from "./guard";
|
|
17
|
+
export { LOCK_SCRIPT, LOCK_STYLE, lockPageHtml } from "./lockPage";
|
|
18
|
+
export { MASTER_LOCK_PRINCIPAL_KEY_PREFIX, MasterLockDirectory, masterLockPrincipalKey, } from "./principals";
|
|
19
|
+
export { MasterLock, readRecord, } from "./masterLock";
|
|
20
|
+
export { DEFAULT_SEED_IDLE_MS, generateStagePassword, mintMasterLockSeed, serializeMasterLockSeed, } from "./seed";
|
|
21
|
+
export { MASTER_LOCK_SETTING_KEY, createKvMasterLockStore, createMemoryMasterLockStore, } from "./store";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Everything the page needs to know about the app it is standing in front of. */
|
|
2
|
+
export interface LockPageOptions {
|
|
3
|
+
/** What to call this app — "Collections", "binary-server inspector". */
|
|
4
|
+
appLabel: string;
|
|
5
|
+
/** Shown under the title. Defaults to the sentence that explains the second wall. */
|
|
6
|
+
note?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Render the CHOOSE-a-password form rather than the type-it form.
|
|
9
|
+
*
|
|
10
|
+
* Set by the guard from `lock.awaitingEnrollment`, i.e. per-person mode with no record
|
|
11
|
+
* for this account yet. It is a rendering hint and nothing more — the server refuses
|
|
12
|
+
* `POST /__lock/enroll` on its own record's state, so a page asking to enroll cannot
|
|
13
|
+
* make enrollment legal.
|
|
14
|
+
*/
|
|
15
|
+
enroll?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The document. Deliberately says the app's NAME and nothing else about it — whoever is
|
|
19
|
+
* looking already reached the origin, so the hostname told them that much, and a page that
|
|
20
|
+
* would not say which app it is cannot tell the owner which password to type.
|
|
21
|
+
*/
|
|
22
|
+
export declare function lockPageHtml(options: LockPageOptions): string;
|
|
23
|
+
/** The stylesheet. Follows the viewer's theme and commits to nothing else. */
|
|
24
|
+
export declare const LOCK_STYLE = ":root{color-scheme:light dark;\n --ml-bg:#f4f5f7;--ml-card:#ffffff;--ml-text:#14171c;--ml-muted:#69707c;--ml-border:#d9dde3;\n --ml-accent:#1f6feb;--ml-accent-text:#ffffff;--ml-danger:#b42318;\n --ml-shadow:0 18px 48px rgba(16,18,22,.14)}\n@media (prefers-color-scheme:dark){:root{\n --ml-bg:#0d0f13;--ml-card:#171a20;--ml-text:#e7eaef;--ml-muted:#8b93a1;--ml-border:#2b3039;\n --ml-accent:#4f8cf7;--ml-accent-text:#0b0d11;--ml-danger:#f97066;\n --ml-shadow:0 18px 48px rgba(0,0,0,.55)}}\n*{box-sizing:border-box}\nbody{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px;\n background:var(--ml-bg);color:var(--ml-text);\n font:15px/1.55 ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",sans-serif}\n.card{width:100%;max-width:23rem;background:var(--ml-card);border:1px solid var(--ml-border);\n border-radius:16px;padding:28px 26px 24px;box-shadow:var(--ml-shadow);text-align:center}\n.glyph{font-size:30px;line-height:1;margin-bottom:10px}\nh1{margin:0 0 6px;font-size:18px;font-weight:650;letter-spacing:-.01em}\n.note{margin:0 0 20px;font-size:13px;color:var(--ml-muted)}\n.field{display:block;text-align:left;margin-bottom:14px}\n.field span{display:block;font-size:12px;font-weight:600;color:var(--ml-muted);margin-bottom:6px}\ninput{width:100%;height:40px;padding:0 12px;border:1px solid var(--ml-border);border-radius:9px;\n background:var(--ml-bg);color:inherit;font:inherit}\ninput:focus{outline:2px solid var(--ml-accent);outline-offset:1px;border-color:transparent}\nbutton{width:100%;height:40px;border:0;border-radius:9px;background:var(--ml-accent);\n color:var(--ml-accent-text);font:inherit;font-weight:600;cursor:pointer}\nbutton[disabled]{opacity:.6;cursor:progress}\n.status{margin:14px 0 0;min-height:1.2em;font-size:12.5px;color:var(--ml-muted)}\n.status[data-tone=\"error\"]{color:var(--ml-danger)}\n";
|
|
25
|
+
/**
|
|
26
|
+
* The page's script.
|
|
27
|
+
*
|
|
28
|
+
* Derives in the browser and sends only the verifier — the password itself never crosses
|
|
29
|
+
* the network, which is what makes "saved nowhere" true rather than aspirational.
|
|
30
|
+
*
|
|
31
|
+
* 600,000 PBKDF2 rounds take a beat on a phone, so the button says so; a page that looks
|
|
32
|
+
* frozen gets clicked again, and every extra click is another 600k rounds.
|
|
33
|
+
*/
|
|
34
|
+
export declare const LOCK_SCRIPT: string;
|