mirror of
https://github.com/PR0M3TH3AN/bitgate.git
synced 2026-10-27 08:31:00 +00:00
Widget styles now prefer --bitgate-* custom properties, falling back to the deprecated --gov-* names and then the existing defaults so current pages and overrides keep working. Browser bundles and ESM entry points expose VERSION (and window.bitgate.version) from package.json. Document allowlist on the widget policy preset list. Sync the lockfile so npm ci sees @bitgate/standalone. Co-authored-by: thePR0M3TH3AN <[email protected]>
315 lines
15 KiB
Markdown
315 lines
15 KiB
Markdown
# BitGate — open items
|
|
|
|
Updated: 2026-08-10
|
|
|
|
BitGate is the standalone governance and policy engine in this repository. The
|
|
existing `@bitgate/core`, `@bitgate/nostr`, `@bitgate/runtime`, `@bitgate/verify`,
|
|
`@bitgate/testing`, `@bitgate/widget`, and compatibility packages are existing
|
|
surfaces. The distribution and API work below must be additive and must not
|
|
break current BitGate consumers or the behavior characterized from BitVid and
|
|
BitRoad.
|
|
|
|
## Priority 1 — Distribution
|
|
|
|
- [ ] Guarantee one browser-ready standalone build: `bitgate.js`.
|
|
- [ ] Require no npm at runtime.
|
|
- [ ] Require no runtime dependencies.
|
|
- [ ] Work directly from vanilla HTML.
|
|
- [ ] Publish predictable downloadable release artifacts.
|
|
- [ ] Provide both an ES module and an npm package.
|
|
- [ ] Verify the standalone build works from ordinary static HTTPS hosts and
|
|
localhost development without requiring a framework or bundler.
|
|
- [ ] Document `file://` limitations and the recommended local development
|
|
server path.
|
|
|
|
## Priority 2 — Canonical API
|
|
|
|
- [ ] Establish one obvious entry point, for example:
|
|
|
|
const decision = await gate.evaluate({
|
|
viewer,
|
|
item,
|
|
action: "view"
|
|
});
|
|
|
|
- [ ] Standardize decision output:
|
|
|
|
{
|
|
visibility,
|
|
ranking,
|
|
interaction,
|
|
transaction,
|
|
reasons,
|
|
evidence
|
|
}
|
|
|
|
- [ ] Keep the four decision axes independent:
|
|
- [ ] visibility
|
|
- [ ] ranking
|
|
- [ ] interaction
|
|
- [ ] transaction
|
|
- [ ] Do not collapse all policy outcomes into `allowed`/`blocked`.
|
|
- [ ] Define stable machine-readable reason codes, for example:
|
|
- [ ] `USER_MUTED`
|
|
- [ ] `FOLLOWED_USER_REPORTED`
|
|
- [ ] `MALWARE_SIGNAL`
|
|
- [ ] `SPAM_SIGNAL`
|
|
- [ ] `POLICY_BLOCK`
|
|
- [ ] Define how any legacy adapter verdicts map onto the canonical four-axis
|
|
decision without losing information.
|
|
- [ ] Define deterministic behavior when an axis is not applicable to an
|
|
action, such as transaction policy for a read-only view.
|
|
|
|
## Priority 3 — Shared Context
|
|
|
|
- [ ] Accept a standardized viewer context:
|
|
|
|
{
|
|
pubkey,
|
|
follows,
|
|
mutes,
|
|
relays,
|
|
trustGraph,
|
|
interests
|
|
}
|
|
|
|
- [ ] Avoid requiring BitGate-specific conversions when data already comes
|
|
from BitLogin or Nostr.
|
|
- [ ] Define optional versus required context fields and safe defaults for
|
|
missing, stale, or unavailable context.
|
|
- [ ] Keep context normalization separate from policy evaluation so applications
|
|
can pass through native BitLogin/Nostr structures without needless copying.
|
|
- [ ] Define privacy boundaries for context: do not require raw viewer identity,
|
|
cross-site tracking data, or unnecessary behavioral history.
|
|
|
|
## Priority 4 — Policies
|
|
|
|
- [ ] Formalize the `bitgate/1` policy schema.
|
|
- [ ] Make policies serializable, hashable, signable, publishable, and forkable.
|
|
- [ ] Allow policies to be loaded from signed Nostr events.
|
|
- [ ] Verify policy signatures, schema versions, hashes, and issuer metadata
|
|
before using a policy; fail safely when verification is ambiguous.
|
|
- [ ] Support named policy profiles, for example:
|
|
- [ ] `social`
|
|
- [ ] `comments`
|
|
- [ ] `marketplace-browse`
|
|
- [ ] `marketplace-checkout`
|
|
- [ ] `messaging`
|
|
- [ ] Keep app-specific policy configuration outside the core engine.
|
|
- [ ] Define policy precedence, composition, conflict resolution, and explicit
|
|
override rules when multiple policies apply.
|
|
- [ ] Make policy evaluation deterministic and testable for the same input,
|
|
policy set, and evaluation time.
|
|
- [ ] Document policy expiry, revocation, cache behavior, and offline behavior.
|
|
|
|
## Priority 5 — Explainability
|
|
|
|
- [ ] Make every decision explainable.
|
|
- [ ] Return machine-readable reasons.
|
|
- [ ] Return optional human-readable explanations without making UI copy part
|
|
of the stable machine contract.
|
|
- [ ] Support `gate.explain(decision)`.
|
|
- [ ] Make it straightforward for applications to answer:
|
|
- [ ] “Why was this hidden?”
|
|
- [ ] “Why can't I reply?”
|
|
- [ ] “Why is checkout disabled?”
|
|
- [ ] Include enough evidence references to audit a decision without exposing
|
|
unnecessary private viewer data.
|
|
- [ ] Distinguish the policy reason, the evidence source, and the resulting
|
|
action; do not imply that a signal is proof when it is only a heuristic.
|
|
- [ ] Define stable explanation behavior for conflicting signals, unavailable
|
|
evidence, expired policies, and fail-open/fail-closed choices.
|
|
|
|
## Priority 6 — Documentation
|
|
|
|
- [ ] Add a vanilla HTML example.
|
|
- [ ] Add a BitLogin + BitGate example.
|
|
- [ ] Add a social feed example.
|
|
- [ ] Add a commerce example.
|
|
- [ ] Document the difference between:
|
|
- [ ] hide
|
|
- [ ] downrank
|
|
- [ ] warn
|
|
- [ ] interaction deny
|
|
- [ ] transaction deny
|
|
- [ ] Document the canonical API in relation to the existing package APIs and
|
|
how current consumers migrate without a forced rewrite.
|
|
- [ ] Document the security and privacy boundaries of local policy evaluation,
|
|
signed Nostr policies, and remotely supplied evidence.
|
|
|
|
## Compatibility and testing gates
|
|
|
|
- [ ] Preserve existing package APIs while introducing the canonical BitGate
|
|
API.
|
|
- [ ] Preserve the behavior characterized from BitVid and BitRoad during
|
|
extraction and future policy-engine changes.
|
|
- [ ] Add unit tests for canonical decision-axis independence, stable reason
|
|
codes, legacy verdict mapping, policy precedence, and explainability.
|
|
- [ ] Add tests for signed policy verification, malformed policies, unknown
|
|
schema versions, expired policies, and ambiguous evidence.
|
|
- [ ] Add browser integration tests for the standalone `bitgate.js` artifact
|
|
from vanilla HTML.
|
|
- [ ] Test the npm package and ES-module import paths from a clean consumer
|
|
project.
|
|
- [ ] Test the definition-of-done examples against the published artifact.
|
|
- [ ] Run the existing build, typecheck, and test suite before and after
|
|
changes; standalone work must not replace or mutate current package
|
|
output.
|
|
|
|
## Definition of done
|
|
|
|
A developer can drop `bitgate.js` into a page, pass it a viewer, item, and
|
|
action, and receive a consistent, explainable policy decision without needing
|
|
to understand BitGate internals. The same canonical engine is available as an
|
|
ES module and npm package, while existing BitGate consumers continue to work
|
|
unchanged.
|
|
|
|
## Open design decisions
|
|
|
|
- [ ] Choose the exact `bitgate/1` event kind and signed-policy event format.
|
|
- [ ] Choose the canonical TypeScript types and runtime validation strategy.
|
|
- [ ] Decide whether `gate.explain(decision)` is pure/local only or may resolve
|
|
additional evidence asynchronously.
|
|
- [ ] Decide default fail-open/fail-closed behavior per axis and per action.
|
|
- [ ] Decide how policy profile discovery works without creating a centralized
|
|
authority or leaking private application context.
|
|
- [ ] Decide whether standalone output is generated from the canonical engine
|
|
directly or from a separately maintained browser entry point.
|
|
- [x] Decide how browser builds expose version and integrity metadata.
|
|
`VERSION` + `window.bitgate.version` equal `package.json`, injected at
|
|
build time. Integrity manifests remain open (distribution / D7).
|
|
|
|
## BitBlocks App Kit compatibility layer
|
|
|
|
Do this only after BitLogin, BitGate, and BitFeed each have clean standalone
|
|
APIs. BitBlocks is an optional integration layer, not a replacement for any of
|
|
the three libraries. It must not merge their implementations or make an app
|
|
depend on the kit when it only needs BitGate.
|
|
|
|
- [ ] Create a tiny optional BitBlocks integration layer.
|
|
- [ ] Keep BitLogin, BitGate, and BitFeed as separate replaceable libraries;
|
|
the App Kit must compose them rather than absorb them.
|
|
- [ ] Provide shared initialization, for example:
|
|
|
|
const app = await BitBlocks.create({
|
|
login: {},
|
|
gate: {},
|
|
feed: {},
|
|
relays: [...]
|
|
});
|
|
|
|
- [ ] Standardize the viewer context exchanged between the components without
|
|
changing BitGate's policy engine, signed-policy format, or four-axis
|
|
decision contract.
|
|
- [ ] Centralize optional common Nostr plumbing in the kit: relay connections,
|
|
subscriptions, EOSE handling, reconnects, deduplication, timeouts,
|
|
publishing, OK responses, profile lookup, follow-list lookup, and
|
|
mute-list lookup.
|
|
- [ ] Expose `app.viewer`, `app.signer`, `app.relays`, `app.gate`, and
|
|
`app.feed` through documented interfaces.
|
|
- [ ] Keep each underlying library independently usable and independently
|
|
versioned.
|
|
- [ ] Do not make applications depend on BitBlocks when they use only
|
|
BitGate's policy and governance capabilities.
|
|
- [ ] Add integration tests proving the kit is an adapter over the standalone
|
|
APIs, not a second implementation of identity, policy, or feed logic.
|
|
|
|
### BitBlocks definition of done
|
|
|
|
A developer or AI agent can take `bitlogin.js`, `bitgate.js`, `bitfeed.js`, and
|
|
`bitblocks.js` and build a functional static Nostr application with almost no
|
|
infrastructure or integration boilerplate, while each individual library
|
|
remains usable without BitBlocks.
|
|
|
|
## Feedback from Tessera building a moderation layer (2026-08-11)
|
|
|
|
Tessera needed exactly what BitGate provides (moderation as policy over public
|
|
Nostr events) but ended up building a **client-side stand-in** — a kind-33604
|
|
"moderation decision" event carrying the four dimensions, ladder-max
|
|
composition, a signed root-controlled moderator set, and a browser console —
|
|
rather than integrating `@bitgate/*`. Notes on why, and what would have made
|
|
adoption the obvious choice.
|
|
|
|
### Why we didn't reach for BitGate first
|
|
|
|
- **[ADDRESSED 2026-08-11] No obvious "just the engine + events over Nostr" path
|
|
for a static site.** The headless path existed but was buried under the widget
|
|
on-ramp. Added a "Headless, straight from NIP-32 labels" subsection to the
|
|
README (four pure functions: decode → contributions → reduce → evaluate, no
|
|
runtime transport, no DOM) and a runnable `examples/headless-quickstart.mjs`.
|
|
Pinned by `tests/headless-quickstart.test.js`.
|
|
- **[ADDRESSED 2026-08-11] The decision event format is ours, not yours.** There
|
|
was no need to invent 33604: **NIP-32 label events (kind 1985) are the
|
|
canonical signed decision format** — `@bitgate/nostr` already has the codec
|
|
(`encodeLabel`/`decodeLabels`/`labelsToContributions`) and `docs/labels.md`
|
|
documents interop. The README and the headless example now say so explicitly
|
|
and round-trip real 1985 events into a four-dimension decision, so other
|
|
clients interoperate instead of each inventing a shape.
|
|
|
|
### High-value asks
|
|
|
|
- **[DONE 2026-08-11] Headless quickstart**: shipped `examples/headless-quickstart.mjs`
|
|
and a README subsection — `@bitgate/core` composing a decision from an array
|
|
of kind-1985 label events + an honored moderator set + a policy, zero DOM. It
|
|
shows exactly the `transaction`-dimension case the ask names: a malware plugin
|
|
stays **visible (warn)** but **not installable (transaction deny)** — where a
|
|
`hidden` boolean can't reach. Pinned by `tests/headless-quickstart.test.js`
|
|
(incl. that an out-of-set labeller's deny is ignored).
|
|
- **[DONE 2026-08-11] Moderator-set as data.** Shipped `resolveAuthorityRoster`
|
|
(`@bitgate/nostr`): resolves the honored actor→roles map from signed kind-30000
|
|
"role set" events and spreads straight into `createAuthorityState`. It honors
|
|
**only** root-signed sets, plus role-based delegation (`signer: "moderator"`
|
|
lets a resolved moderator publish a further set, one ordered pass), keeps the
|
|
latest set per publisher (replaceable), and ignores anyone unauthorized — so
|
|
moderators change by publishing a signed set, no redeploy. Covered by
|
|
`roster.test.js` (root-only, unauthorized-ignored, delegation, replaceable,
|
|
and feeds `createAuthorityState`); README "headless" section shows it.
|
|
- **[DONE 2026-08-11] Allowlist / whitelist mode.** The engine already honored
|
|
`requireAllowlist` + `allowlistMiss` (evaluator.js), and the commerce example
|
|
configured a seller allowlist by hand — but there was no reusable named
|
|
preset. **Shipped** `ALLOWLIST_POLICY` (registry name `allowlist`): only
|
|
publishers with a `user-allow` contribution are shown, every unlisted one is
|
|
hidden across all four dimensions, administrative denials still apply on top,
|
|
and protected actors (root/mods) are never gated. Symmetric to the denylist —
|
|
populate `userAllow` the same way (signed contributions / NIP-32 allow
|
|
labels). Covered by presets.test.js (unlisted hidden, allowlisted shown,
|
|
protected bypasses); surfaced in the README preset list.
|
|
|
|
### Medium
|
|
|
|
- **[DONE 2026-08-11]** Ship a standalone ESM single-file. **Shipped**
|
|
`@bitgate/standalone` (`packages/standalone/`, `npm run build:standalone` →
|
|
`dist/bitgate.js`, ~44 KB): the headless engine (core) + the Nostr codecs
|
|
(nostr, incl. the kind-1985 label round-trip) esbuilt into one self-contained,
|
|
dependency-free ESM file — `@bitgate/verify`'s @noble deps are deliberately
|
|
not pulled in. A no-build static site vendors the single file and gets the
|
|
whole headless path. Verified dependency-free (no bare imports in the output)
|
|
and end-to-end (a smoke test composes a decision from the entry). Documented
|
|
in the README Packages table and the headless section; wired into `npm run build`.
|
|
- **[DONE 2026-08-11]** A worked example of BitGate feeding a feed ranking.
|
|
**Shipped** `examples/feed-ranking.mjs` (runnable): it reads `decision.ranking`
|
|
(`{ effect: "normal" | "downrank", weight }`) and turns it into a score
|
|
multiplier (`base ** weight` for a downrank), so trusted spam reports push two
|
|
high-base authors below a clean lower-base one **without hiding them** —
|
|
visibility stays a separate dimension, capped at `restrict` on a feed. The
|
|
multiplier lives in the example, not the engine (core ships no such numbers).
|
|
Pinned by `tests/feed-ranking.test.js`; the README headless section points to
|
|
it.
|
|
|
|
## Design review — external critique (2026-08-11)
|
|
|
|
From an outside review of the BitBlocks stack. BitGate was called "underrated."
|
|
|
|
- **[agree — positioning, NOT new scope] Position BitGate as a general policy
|
|
evaluation engine, not "moderation."** The primitive already answers "given
|
|
this actor, object, context, and evidence, what is permitted?" — which spans
|
|
permissions, marketplace-fraud controls, age/reputation gates, community
|
|
rules, transaction policy, content warnings, and app governance, not just
|
|
hide/allow. This is a DOCS/framing task: lead the README with the general
|
|
framing and add a couple of non-moderation worked examples (e.g. an
|
|
access/permission gate, a transaction-policy gate). **Deliberately not a
|
|
feature request** — the engine is already general; per the stack-wide
|
|
"resist over-expansion" discipline, reframe rather than expand.
|
|
- The four-dimension decision model and the trust-vs-authority split were called
|
|
out as the strong, differentiated ideas — lead with them.
|