mirror of
https://github.com/PR0M3TH3AN/bitlogin.git
synced 2026-10-27 08:31:21 +00:00
Builds out the v0.3 spec as an npm workspace monorepo any static site can consume for login and Nostr signing: - packages/core: framework-free protocol implementation — Argon2id/HKDF/ ScalarExpand/AES-256-GCM/JCS crypto primitives, NIP-01 event handling and a quorum-aware relay pool, credential/recovery capsule construction and validation, and the full account lifecycle (registration, any-device login with rollback/relay-disagreement detection, phrase recovery with recovery-capsule refresh, password rotation with mandatory tombstone + NIP-09 deletion, replica repair, recovery export, NIP-19 encoding, a NIP-44 v2 implementation, and a NIP-07-shaped signer). 49 tests cover the spec's Phase 0 byte-level vectors plus full create/login/recover/rotate end-to-end scenarios against an in-memory mock relay. - packages/widget: a <bitlogin-auth> custom element with a dedicated crypto Web Worker (private keys never touch the main thread) and a window.nostr NIP-07-compatible provider, so existing Nostr web apps work against it with zero code changes. Built with Vite into two relocatable static files. - packages/demo: a plain static site (no build step of its own) consuming the widget — a branding/landing page, an integration guide, and an account-manager page covering create/login/recover, password rotation, and identity export. All flows were verified twice: protocol-level tests against a mock relay, and the actual built widget driven end-to-end in real Chromium (registration, phrase confirmation, event signing, clean-device login, window.nostr, and phrase recovery all reproduce the identical everyday pubkey). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GWJbXXhvTx9wo59jSz57J6
60 lines
1.9 KiB
TypeScript
60 lines
1.9 KiB
TypeScript
/** IndexedDB-backed KeyValueStore (§21) — usable from the main thread or a Web Worker. */
|
|
import type { KeyValueStore } from "@bitlogin/core/storage";
|
|
|
|
const DB_NAME = "bitlogin";
|
|
const STORE_NAME = "kv";
|
|
const DB_VERSION = 1;
|
|
|
|
function openDb(): Promise<IDBDatabase> {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
request.onupgradeneeded = () => {
|
|
const db = request.result;
|
|
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
db.createObjectStore(STORE_NAME);
|
|
}
|
|
};
|
|
request.onsuccess = () => resolve(request.result);
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
}
|
|
|
|
export class IndexedDbKeyValueStore implements KeyValueStore {
|
|
private dbPromise: Promise<IDBDatabase> | null = null;
|
|
|
|
private db(): Promise<IDBDatabase> {
|
|
if (!this.dbPromise) this.dbPromise = openDb();
|
|
return this.dbPromise;
|
|
}
|
|
|
|
async get(key: string): Promise<string | undefined> {
|
|
const db = await this.db();
|
|
return new Promise((resolve, reject) => {
|
|
const tx = db.transaction(STORE_NAME, "readonly");
|
|
const request = tx.objectStore(STORE_NAME).get(key);
|
|
request.onsuccess = () => resolve(request.result as string | undefined);
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
}
|
|
|
|
async set(key: string, value: string): Promise<void> {
|
|
const db = await this.db();
|
|
return new Promise((resolve, reject) => {
|
|
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
tx.objectStore(STORE_NAME).put(value, key);
|
|
tx.oncomplete = () => resolve();
|
|
tx.onerror = () => reject(tx.error);
|
|
});
|
|
}
|
|
|
|
async delete(key: string): Promise<void> {
|
|
const db = await this.db();
|
|
return new Promise((resolve, reject) => {
|
|
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
tx.objectStore(STORE_NAME).delete(key);
|
|
tx.oncomplete = () => resolve();
|
|
tx.onerror = () => reject(tx.error);
|
|
});
|
|
}
|
|
}
|