Critical
- Any pubkey could publish a kind:30078 roles document and become root of a
deployment, granting itself every capability and denying anyone. Roles are now
accepted only from the configured root, compared against the constructor
value rather than current authority state so a bad ingest cannot move the
goalposts. Reproduced end to end before the fix; regression test added.
- Administrative state now fails closed without signature verification. Without
it, event.pubkey is an unauthenticated claim, so an authorship check alone
proves nothing — a relay can put the root's key on an event the root never
wrote. trustUnsignedEvents opts in for local and test use.
High
- Report categories could evade moderation entirely: resolveThresholds used
bracket truthiness, so "constructor" resolved to the Object constructor (all
gates undefined) and "__proto__" to Object.prototype, both bypassing the
default threshold. Own-property lookups and null-prototype tables throughout
core — policy, evaluator, evidence, authority, presets.
- Live subscriptions bypassed signature verification entirely; an attacker only
had to wait for the subscription rather than the load.
- The default administrative query asked relays for every kind:30078 and
kind:30000 event in existence, with no author, identifier, or limit. Now
filtered to governance identifiers with a bounded limit.
Medium
- Cached state was trusted as authority: a cache naming a different root is now
rejected, so same-origin tampering cannot persist a takeover.
- Provider attribute changes silently stopped working after the first one, and
concurrent starts leaked a transport. Generation-guarded.
- Unbounded growth in the report store, mute store, decision cache, and the
transport's per-subscription dedupe set — all now capped.
Also restores the on-wire `d` identifier to <namespace>:governance:<scope>:v1.
The BitGate rename replaced "governance:" too broadly and silently changed the
event format; both encoder and parser changed together, so round-trip tests
never noticed. The identifier names the kind of document, not the library.
Adds docs/security.md and 21 regression tests reproducing each finding.
832 tests.