11 Commits
Author SHA1 Message Date
thePR0M3TH3ANandClaude Opus 4.8 af2818fbce core: add the allowlist policy preset
The engine already honored requireAllowlist + allowlistMiss, and the
commerce example configured a seller allowlist by hand -- but there was no
reusable, named "only show allowlisted publishers" preset, the symmetric
twin of the denylist. Integrators (Tessera) punted on allowlist mode for
lack of one.

Add ALLOWLIST_POLICY (registry name "allowlist"): a publisher is shown only
when an authorized actor has contributed a user-allow for them, and every
unlisted publisher misses and is hidden across all four dimensions.
Administrative denials still apply on top, and protected actors (root,
moderators) are never gated -- the people who curate the list can always
see. Populate userAllow exactly as the denylist: signed contributions
resolved from relays (e.g. NIP-32 allow labels).

Exported from @bitgate/core, registered in POLICY_PRESETS, documented in the
README, and covered by presets.test.js (unlisted hidden, allowlisted shown,
protected bypasses).

Addresses the "allowlist / whitelist mode" item of the Tessera integration
feedback (docs/TODO.md).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01BXQCvJPA6sVSQ5j8uBd9bB
2026-08-11 20:30:52 -04:00
thePR0M3TH3AN 7a45f1cf3e feat(nostr): outbox-model fetching, private mutes, contacts, NIP-56 vocabulary
Closes the four spec gaps from the audit, verified against the NIPs repo.

NIP-65 (kind 10002) — the one that was actually breaking things. Mute lists are
published by each contact to their own write relays, so querying a fixed relay
set finds only the subset who happen to write where this deployment reads. The
trust graph was silently under-fed and counts came out low, which looks like
"nobody muted them" rather than like a bug. Adds a relay-list codec, per-author
grouping with a fallback for accounts that have published no list, and
on-demand relay connections in the transport so an outbox query can reach
relays that were never configured.

NIP-51 private entries — the viewer's own private mutes are now decrypted
through their signer (NIP-44) and applied as viewer mutes. Someone who mutes
privately previously got no effect at all. The privacy boundary is asymmetric
and enforced at the codec: another account's private list is never decoded,
and the check lives there rather than in the caller's discipline.

NIP-02 (kind 3) — the runtime can build the trust graph itself, so
<bitgate-provider> no longer needs a host to supply a follow list. Set
trust="manual" to opt out.

NIP-56 vocabulary — SOCIAL_POLICY declared thresholds for "harassment", which
is not a NIP-56 type, so no standard client could ever trigger it. Replaced
with the defined vocabulary and asserted in tests. Commerce keeps its
marketplace-specific categories, now documented as deliberate, with defaults so
unclassified reports are never silently dropped.

Two bugs found by the new tests:
- checkout had no default threshold, so a flood of "other" reports did nothing
  at the one place money changes hands. Now earns review, never auto-denial.
- describe() spread diagnostics after a derived field, so a stale zero shadowed
  the live relay-list count.

Adds docs/nips.md recording coverage, deliberate choices, and remaining gaps —
NIP-32 labelling being the largest.

811 tests.
2026-08-09 20:52:21 -04:00
thePR0M3TH3AN adeba151b8 feat: rename to BitGate and close the drop-in gap
Renames the project and makes it as easy to adopt as BitLogin, which was the
stated bar and which it did not previously meet.

Rename (ADR 0005): @nostr-governance/* -> @bitgate/*, governance-* elements ->
bitgate-*, createGovernanceRuntime -> createBitGate. "Governance" reads as DAO
voting to this audience and named a category rather than a product; BitGate
names the benefit and joins the bit- family that BitVid and BitRoad compose
with. Domain types keep their names — the product is BitGate, the subject
matter is still governance. The migration plan is left as written; this ADR
records the change rather than rewriting history.

Drop-in distribution (ADR 0006): adopting BitGate meant supplying a transport,
authoring a policy, and wiring every element in JavaScript. Now:

- createRelayTransport(urls) — WebSocket transport with reconnection, backoff,
  subscription replay, and cross-relay dedupe
- policy presets (social, commerce, admin-only) addressable by name; the engine
  still carries no thresholds of its own
- <bitgate-provider> — configuration in markup, with descendants finding the
  runtime through a bubbling context event rather than DOM walking, which
  breaks across shadow boundaries
- npm run build:widget — a self-contained dist/bitgate.js

That last one came from running it in a browser rather than from a test. Bare
specifiers resolve in Node and vitest but not in a browser, so the widget
source worked everywhere except a plain static page — the one place it exists
to serve. The bundle is not committed: consumers build from reviewed source.

Also found by running it: the provider's ready event fires during element
upgrade, before a page that defines elements after parsing can attach a
listener, so it now exposes `ready` as a promise too.

Four bugs fixed along the way, three found by new tests and one by the browser:
admin state stayed silent when a roster change altered capabilities but no
denials; the relay transport sent every subscription twice on connect; the
commerce preset computed a meaningless visibility verdict at checkout and could
hide a listing from its own seller; <bitgate-status> threw on an unusable
decision.

759 tests; lint, typecheck, and both builds clean.
2026-08-09 19:58:45 -04:00
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
thePR0M3TH3AN 4f689184e2 feat(core): rebuild governance core to the plan's decision model
The evaluator was a skeleton with invented thresholds (5/3/10/5) and two
unimplemented branches. Rebuild it against the migration plan's spec and the
behavior characterized from the pinned reference commit.

Core:
- four independent decision dimensions (ranking, visibility, interaction,
  transaction) with severity ladders and max-merge composition
- stable reason identifiers replacing free-text strings
- structured evidence, redacted unless a profile opts in
- administrative-state reduction with capability filtering at merge time, so
  revoking a role drops that actor's entries immediately
- protected actors stripped from every denial set
- snapshot fingerprints for cache invalidation
- generic policy engine: applications supply profiles and category thresholds;
  the core carries none of its own

Characterization:
- 34 generated fixtures covering every behavior the plan's Phase 0 requires
- conformance suite that actually evaluates them, replacing a placeholder that
  asserted a list's length
- reference thresholds (3/3/20/5, 60-day mute window) live in bitvid-compat as
  an application profile, not as engine defaults

Fixes found while building:
- reduceAdminState accepted malformed target identifiers, because getTargetKey
  formats anything it is given
- community-user-deny fired for any contributor rather than only for
  community-curated sources

273 tests pass; typecheck and the reference-import guard are clean.
2026-08-09 17:33:41 -04:00
thePR0M3TH3AN 5a7c1a26de feat(core): implement evaluator module with governance decision making 2026-07-26 19:17:38 -04:00
thePR0M3TH3AN b24500010f feat(core): implement policy module with decision making and effect composition 2026-07-26 19:11:49 -04:00
thePR0M3TH3AN 919d94c7a9 feat(core): implement authority module with role and capability management 2026-07-26 19:09:07 -04:00
thePR0M3TH3AN ec03aab0ab feat(core): implement targets module with target validation and hierarchy functions 2026-07-26 19:05:44 -04:00
thePR0M3TH3AN 7bf304db2c feat(core): implement identifiers module with target creation and normalization 2026-07-26 19:02:33 -04:00
thePR0M3TH3AN 10326a2804 fix: make workspace install under npm and enforce real guardrails
Replace pnpm-only workspace: protocol with npm workspace deps, pin BitVid and
BitRoad reference commits, add Node types, and make the reference-import check
scan package graphs and source for forbidden bitvid/bitroad imports.
2026-07-26 18:55:21 -04:00