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
47 lines
1.5 KiB
JavaScript
47 lines
1.5 KiB
JavaScript
// Minimal zero-dependency static file server for local preview of dist/.
|
|
import { createServer } from "node:http";
|
|
import { readFile } from "node:fs/promises";
|
|
import { existsSync, statSync } from "node:fs";
|
|
import { fileURLToPath } from "node:url";
|
|
import { dirname, extname, join } from "node:path";
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const root = join(__dirname, "dist");
|
|
const port = process.env.PORT ? Number(process.env.PORT) : 4173;
|
|
|
|
const MIME = {
|
|
".html": "text/html; charset=utf-8",
|
|
".js": "text/javascript; charset=utf-8",
|
|
".css": "text/css; charset=utf-8",
|
|
".json": "application/json; charset=utf-8",
|
|
".svg": "image/svg+xml",
|
|
".ico": "image/x-icon",
|
|
".webmanifest": "application/manifest+json"
|
|
};
|
|
|
|
createServer(async (req, res) => {
|
|
try {
|
|
let path = decodeURIComponent(new URL(req.url, "http://localhost").pathname);
|
|
let filePath = join(root, path);
|
|
if (!filePath.startsWith(root)) {
|
|
res.writeHead(403);
|
|
res.end("Forbidden");
|
|
return;
|
|
}
|
|
if (existsSync(filePath) && statSync(filePath).isDirectory()) {
|
|
filePath = join(filePath, "index.html");
|
|
}
|
|
if (!existsSync(filePath)) {
|
|
filePath = join(root, "index.html");
|
|
}
|
|
const body = await readFile(filePath);
|
|
res.writeHead(200, { "Content-Type": MIME[extname(filePath)] ?? "application/octet-stream" });
|
|
res.end(body);
|
|
} catch (err) {
|
|
res.writeHead(500);
|
|
res.end(String(err));
|
|
}
|
|
}).listen(port, () => {
|
|
console.log(`BitLogin demo running at http://localhost:${port}`);
|
|
});
|