Several contracts existed and were tested in isolation while nothing actually used them. Reported as complete previously; this corrects that. - storage was injected and never referenced. Adds persist/hydrate, hydration before querying relays, and last-known-good state with a stale flag when a relay is unreachable, which is what makes the characterized cache-fallback behavior real rather than reduction-level only - signature verification existed as a tested helper the load path never called. Now applied through an injected verifier before state becomes authoritative, with rejection counts and an enabled/disabled readout in diagnostics - trusted mute lists had no subscription flow, so the store never populated in a real deployment. Subscribes by author, chunked - community source references decoded but were never resolved. Curator lists are now fetched and merged with a source marker, still capability-checked - root-published policy was announced and discarded. Now applied, accepted only from the root, and a malformed document leaves the working policy intact - adds import/export and capability queries Two design corrections found while wiring: - the cache key hashed the entire roster, so it rotated whenever a moderator was added and the cache was discarded. Now derived from root identity alone. - the root administrator is a constructor option: it is deployment config, and hydration could not otherwise find its own cache, since the key derives from the very fingerprint the cache would have supplied. 634 tests; lint, typecheck, and the declaration build clean.
7.7 KiB
Checkpoints
Scope note: this repository is the SDK. Migration-plan Phases 6–11 (BitVid shadow mode, cutovers, and duplicate removal) modify the consumer repositories and are tracked there, not here.
Checkpoint 0 — Repo skeleton (completed)
Delivered: npm workspaces monorepo, Node 22 + ESM, Vitest, JSDoc typecheck, GitHub Actions CI, reference import guard, provenance docs.
Later additions: GPL-3.0-or-later LICENSE, CHANGELOG.md, architecture
decision records under docs/adr/, and a working per-package type build. The
types fields previously pointed at paths the build never produced, so
consumers received no declarations; declarations now emit into each package.
Checkpoint 1 — Characterization corpus (completed)
Delivered:
- 34 fixtures under
fixtures/bitvid/cases/, generated byscripts/build-characterization-corpus.mjs - conformance harness (
packages/testing/src/conformance.js) that builds a snapshot, evaluates it, and diffs against a partial expectation - conformance suite (
tests/conformance/bitvid-fixtures.test.js) asserting every required behavior, determinism, and input immutability
Characterized values, read from the pinned reference commit and cross-checked against the migration plan's baseline section:
Trusted report blur threshold: 3
Trusted report autoplay threshold: 3
Trusted mute hide threshold: 20
Trusted spam-report hide threshold: 5
Trusted mute validity window: 60 days
These live in @nostr-governance/bitvid-compat as one application's profile,
not as engine defaults.
Provenance: fixtures are authored from the reference source and its documented configuration, not captured from a live relay. They pin the behavior the extraction must reproduce; they are not a recording of production traffic.
Checkpoint 2 — Pure core (completed)
Delivered: identifiers, targets, authority, administrative-state reduction, reasons, evidence, policy engine, fingerprints, evaluator, adapter contract.
Acceptance:
- identical snapshots produce identical decisions (asserted per fixture)
- evaluation performs no I/O and reads an injected clock
- evaluation mutates no input (asserted per fixture)
- the core imports no browser globals and has no dependencies
- the core contains no application-specific terms or thresholds
Checkpoint 3 — Nostr codecs (completed)
Delivered: bech32/NIP-19 (verified against the NIP-19 test vector), canonical v1
on kind:30078, NIP-56 reports, NIP-51 mute lists, legacy administrative lists
covering all five migration stages, replaceable selection with a deterministic
same-timestamp tiebreak, injected signature verification.
Checkpoint 4 — Runtime stores (completed)
Delivered: GovernanceAdminStore, TrustGraphStore, ReportStore,
TrustedMuteStore, PolicyStore, OverrideStore, GovernanceRuntime.
Acceptance: injected transport, storage, and clock; change-only emission; viewer switching clears viewer-scoped state while keeping operator seeds; subscriptions close on destroy; report subscriptions are chunked (200 per filter) rather than opened per target; author evidence is queryable in batches; expired mute lists prune against the injected clock.
Correction
An earlier revision of this file claimed storage acceptance that was not met: storage was injected and then never used, so nothing persisted or hydrated and the characterized cache-fallback behavior existed only at the reduction level. Closed in Checkpoint 8.
Checkpoint 5 — Public commands (completed)
Delivered: contribution, role, policy, community-source, report, and override commands with stable error codes.
Acceptance: every command checks capability locally before the signer is invoked, so an unauthorized actor cannot produce a signed mutation; revoked actors' contributions stop affecting effective state; protected actors are refused at the command boundary; first relay acceptance completes the command while remaining relay results stay observable.
Checkpoint 6 — Reusability proof (completed)
Delivered: examples/commerce, a marketplace built entirely on the public API,
covering the ten proof-of-fit behaviors the migration plan requires — including
that no core change was needed. Plus README.md, docs/integration-guide.md,
and examples/commerce/README.md.
Checkpoint 7 — Performance and security fixtures (completed)
Delivered:
tests/performance/large-feed.test.js— 5,000 targets, 500 authors, 100 trusted muters per author, multiple report categories and administrative contributors, viewer switching, and roster changestests/security/requirements.test.js— one case per enumerated security requirement testable at this layer
Acceptance:
evaluateMany(5,000)performs no network access- report subscriptions are chunked, never one per card
- updating one report invalidates only that target's decisions
- updating one author's mute state invalidates only that author's targets
- changing policy, roster, or viewer invalidates everything
- destroy leaves no subscriptions or cached decisions
This fixture found a real defect: evaluation was quadratic in state size, because the snapshot and its fingerprint were rebuilt per target. A 5,000-target pass took ~47 seconds. After memoizing both, the same pass takes ~85ms cold and ~6ms warm.
Requirements 15 and 16 (never export signer secrets, avoid shared moderator keys) are properties of the injected signer and of operational practice rather than of this code; the root-authorized contributor model is what removes the need for a shared moderator key.
Checkpoint 8 — Runtime wiring (completed)
Closes the gaps between interfaces that existed and pipelines that used them.
Delivered:
- Storage:
persist()/hydrate(), hydrate-before-query on load, and last-known-good state retained with astaleflag when relays fail. Cache keys are namespaced by root identity — not by the full roster, which would rotate the key every time a moderator was added. - Signature verification wired into the load path through an injected
verifier, with rejected counts in diagnostics and an explicit
signatureVerification: enabled|disabledreadout. - Trusted mute-list subscriptions by author, chunked. Without this the trusted-mute store never populated in a real deployment.
- Community source resolution: curator lists are fetched and merged with a source marker, still subject to the curator's capabilities.
- Root policy application: policy documents are now applied rather than merely announced, accepted only from the root, and a malformed document leaves the working policy in place.
- Import/export (§10.14) exporting contributions rather than conclusions, so an import re-derives effective state against the importing roster.
- Capability queries (§10.8):
can(),capabilitiesOf(),viewerCapabilities().
Design note: the root administrator is now a constructor option, because it is deployment configuration rather than discovered state. Hydration could not otherwise find its own cache — the key derives from the root fingerprint the cache itself would have had to supply.
Status
CI gates green: reference guard, typecheck, 634 tests, per-package type build.
Typecheck and the declaration build both pass from a cold checkout with no
dist/ present.
Not done here
Migration-plan Phases 6–11 operate on BitVid and BitRoad:
- Phase 6 — BitVid compatibility facade and shadow mode
- Phase 7 — cut over BitVid administrative state
- Phase 8 — cut over BitVid reports and trust
- Phase 9 — cut over BitVid policy
- Phase 10 — validate through BitRoad itself
- Phase 11 — remove duplicated BitVid logic
The commerce example stands in for Phase 10's API-shape validation without touching the BitRoad repository.