Files
bitgate/package.json
T
thePR0M3TH3AN 3b95a63058 feat(verify): optional NIP-01 signature verification, on by default in the widget
Closes the gap the audit left open: administrative state fails closed without a
verifier, and BitGate shipped none.

@bitgate/verify is a separate package so core, nostr, and runtime keep no crypto
dependency and can be audited without one. The widget bundles it, so the
drop-in path is verified with no extra step; verify="off" disables it, sets
data-unverified on the element, and emits bitgate:unverified — meant for local
development, and meant to be visible.

Verification recomputes the event id before checking the signature. Checking
only the signature would let a forged event pass by reusing a genuine signature
over an unrelated id, which is the failure mode that matters here.

createVerifier also rejects events dated far in the future. A far-future
created_at wins replaceable-event selection indefinitely and pins stale state in
place; the signature is perfectly valid, so only a clock check catches it.

Found while testing: #adminIngestAllowed checked that a verifier *exists*, not
that this event was verified — so the public ingestEvent path, which
"bring your own relays" integrations use, bypassed verification entirely.
Verification now runs inside ingestEvent, with an already-verified flag for the
loaders. An async verifier cannot be awaited there and is treated as
unverified; ingestVerified() awaits properly.

Conformance validated against 120 live events from a public relay across kinds
0, 1, 3 and 10002 — 59 with non-ASCII content and 71 containing quotes,
newlines or tabs, which is where a serialization mismatch would surface. All
120 verified, zero id mismatches.

Bundle grows 72K -> 120K. 855 tests; audit clean.
2026-08-10 08:34:12 -04:00

36 lines
1.5 KiB
JSON

{
"name": "bitgate",
"version": "0.1.0",
"private": true,
"type": "module",
"description": "Headless Nostr governance SDK for decentralized moderation and access control",
"scripts": {
"test": "npm run test:unit && npm run test:conformance",
"test:unit": "vitest run --passWithNoTests",
"test:conformance": "vitest run tests/conformance --passWithNoTests",
"typecheck": "tsc -p tsconfig.json --noEmit",
"build:types": "tsc -p packages/core/tsconfig.types.json && tsc -p packages/nostr/tsconfig.types.json && tsc -p packages/verify/tsconfig.types.json && tsc -p packages/runtime/tsconfig.types.json && tsc -p packages/bitvid-compat/tsconfig.types.json && tsc -p packages/widget/tsconfig.types.json && tsc -p packages/testing/tsconfig.types.json",
"lint": "npm run check:references && npm run check:types",
"check:references": "node scripts/verify-no-reference-imports.mjs",
"check:types": "tsc -p tsconfig.json --noEmit && tsc -p packages/widget/tsconfig.json --noEmit",
"build:widget": "node packages/widget/build.mjs",
"build": "npm run build:types && npm run build:widget && npm run build:site",
"build:site": "node packages/site/build.mjs",
"dev:site": "node packages/site/build.mjs && node packages/site/serve.mjs"
},
"engines": {
"node": ">=22"
},
"workspaces": [
"packages/*",
"examples/*"
],
"devDependencies": {
"@types/node": "^22.10.0",
"happy-dom": "^20.11.2",
"typescript": "^5.4.5",
"vitest": "^3.2.7",
"esbuild": "^0.28.2"
}
}