Files
bitgate/.agents/memory/architecture.md
2026-08-12 20:57:00 -04:00

1.9 KiB

Architecture

Durable architectural decisions and constraints for BitGate.

Administrative state fails closed without signature verification

With no verifySignature configured, roles, policy, and contribution documents are rejected outright (counted in diagnostics.rejectedUnverified) — event.pubkey is an unauthenticated claim until a signature is checked, so an authorship check alone proves nothing. Verification runs inside ingestEvent itself, not only in the loaders, because ingestEvent is public API; an asynchronous verifier on that synchronous path is treated as unverified ("I could not check this" reads as failure), and ingestVerified() or the loaders must be used to await one. trustUnsignedEvents: true and the widget's verify="off" exist for local development only and are made visible on purpose. Trust signals (reports, mutes) are deliberately not gated this way because they are bounded by the viewer's own follow graph — a weaker guarantee, which is why verification still matters for them.

Authoritative source:

  • docs/security.md ("Signature verification is required")
  • packages/verify/

Relays are transport, not authority

Everything arriving from a relay is untrusted input: a relay can withhold events, replay old ones, and — absent signature verification — fabricate them wholesale, including pubkey. Two independent checks therefore gate administrative state: authorship (a roles document is accepted only from the deployment's configuredRoot, compared against the constructor value and never against current authority state, so a bad ingest cannot move the goalposts) and authenticity (the signature check above). The hostile-input table in the security doc enumerates the attacks this design is meant to survive; consult it before touching ingest, authorship, or verification paths.

Authoritative source:

  • docs/security.md ("The trust boundary", "Hostile input BitGate is designed to survive")