Files
bitgate/docs/adr/0004-hex-only-core.md
T
thePR0M3TH3AN 2beb7a7bb5 feat: adapter contract, commerce proof-of-fit, and reusability docs
Proves the engine did not stay video-specific during extraction.

- application adapter contract in core (createApplicationAdapter,
  evaluateObject, collectTargets): an object names the targets that govern it
  and the strictest verdict reaching any of them wins, so denying a seller
  takes their whole catalogue down with no cascade logic in the application
- examples/commerce: a marketplace on the public API covering all ten
  proof-of-fit behaviors the plan requires, including that no core change was
  needed. One product, one snapshot, four surfaces, four different verdicts.
- README, integration guide, per-example README, CHANGELOG, four ADRs, and
  GPL-3.0-or-later LICENSE (the license was listed in Phase 1 but missing)
- checkpoints rewritten to reflect actual state and to say plainly which
  migration-plan phases belong to the consumer repos rather than here

Fixes a real gap found by the proof-of-fit: composeDecisions kept only the
first decision's evidence, so an object governed by several targets lost the
evidence that drove its verdict. Evidence now merges — counts summed,
contributor lists unioned, flags or-ed.

556 tests; lint, typecheck, and per-package type build clean.
2026-08-09 17:57:09 -04:00

1000 B

ADR 0004 — The core is hex-only

Status: accepted

Context

Administrative lists in the wild contain both hex pubkeys and npub strings, and the reference implementation normalized them everywhere it touched them. Doing the same in the core would mean bech32 decoding in a package that is meant to have no dependencies and no encoding opinions.

Decision

@nostr-governance/core accepts 64-character hex only. Decoding lives in @nostr-governance/nostr (normalizePubkeyInput, normalizeEventIdInput), and codecs normalize at ingest, so the core only ever sees hex.

Consequences

The core stays dependency-free and runs unchanged in Node and the browser.

The trade-off is a real footgun: normalizePubkey returns "" for a valid npub rather than throwing. Callers that hand user input straight to the core will silently get nothing. Anything accepting operator or user input must normalize through the codec package first — this is called out in the integration guide.