mirror of
https://github.com/PR0M3TH3AN/bitgate.git
synced 2026-11-05 04:51:00 +00:00
BitGate read standard events but published a private vocabulary, making it an
island. NIP-32 (kind 1985) is the shared moderation vocabulary; BitGate now
reads and writes it. See ADR 0007.
The design keeps flexibility central, since the goal is a reusable building
block. A label is (namespace, value, target) and carries no built-in meaning —
"deny", "nsfw", and "MIT" are the same kind of object. What a label means is a
policy decision an application supplies via labelMapping { namespace,
denyValues, allowValues }. The codec itself is vocabulary-free; decodeLabels
returns every label regardless of namespace for generic use.
A matching label becomes a contribution authored by the labeller, gated
identically: it denies someone only if the labeller holds the capability in the
roster, exactly like a community source. An untrusted labeller's deny does
nothing, and labels fail closed without verification like any administrative
event.
- consume: ingestEvent maps kind 1985; loadLabels(labellers) fetches them
- emit: commands.publishLabel(target, value), gated by the same capability as
denying, so the shared form is no weaker than the private one
- the widget bundle re-exports the label API for the drop-in path
Validated against 40 real kind:1985 events from public relays (ISO-639-1
language labels among them): all decoded with targets and namespaces, zero
throws. 897 tests; cold gates green; audit clean.
55 lines
2.5 KiB
Markdown
55 lines
2.5 KiB
Markdown
# ADR 0007 — NIP-32 labelling as the interop surface
|
|
|
|
**Status:** accepted
|
|
|
|
## Context
|
|
|
|
BitGate read two standard event kinds (NIP-56 reports, NIP-51 mutes) but
|
|
published its moderation output as a private vocabulary — contribution lists
|
|
under a `bitgate`-namespaced `d` identifier. Other clients could not act on a
|
|
BitGate deployment's moderator decisions, and BitGate could not act on anyone
|
|
else's. For a project whose stated goal is being a reusable building block,
|
|
that made it an island.
|
|
|
|
NIP-32 (kind 1985) is the shared vocabulary for moderation labels: an `L`
|
|
namespace, an `l` value, and `e`/`p`/`a` targets.
|
|
|
|
## Decision
|
|
|
|
Treat a label as **just another wire format for a contribution**, subject to
|
|
the identical capability gate.
|
|
|
|
A NIP-32 label is `(namespace, value, target)`. A BitGate contribution is
|
|
`(kind, target)` that only takes effect if its author holds the matching
|
|
capability. The bridge is `labelsToContributions(labels, mapping)`, where the
|
|
application supplies the namespace it trusts and the label values that mean
|
|
deny or allow. A matching label becomes a contribution authored by the
|
|
labeller, with a `source` marker, and reduces exactly like a community source:
|
|
it denies someone only if the labeller holds the capability in the roster.
|
|
|
|
The codec itself carries no vocabulary. "MIT", "nsfw", and "spam" are all
|
|
labels; whether any of them means denial is a policy decision that lives in the
|
|
caller's `labelMapping`, not in the codec. This is what keeps it a building
|
|
block: an application brings its own namespace and its own words.
|
|
|
|
- **Consume:** `ingestEvent` maps kind 1985 through the configured
|
|
`labelMapping`; `loadLabels(labellers)` fetches them.
|
|
- **Emit:** `commands.publishLabel(target, value)`, gated by the same
|
|
capability as denying the target, so the shared form is no weaker than the
|
|
private one.
|
|
|
|
## Consequences
|
|
|
|
A BitGate deployment can honour a third-party labeller by adding them to the
|
|
roster with a contribution capability — no new trust mechanism, because labels
|
|
reuse the one that already exists. Other clients can read a deployment's
|
|
`deny` labels. The two directions round-trip.
|
|
|
|
Because labels are administrative state, they fail closed without signature
|
|
verification, like every other administrative document.
|
|
|
|
`r` (relay) and `t` (topic) label targets are dropped rather than mapped: they
|
|
have no place in an allow/deny model, and representing them as something else
|
|
would be worse than ignoring them. An application that cares about topic labels
|
|
can consume the codec's generic `decodeLabels` output directly.
|