Skip to content

[Proposal] Thread sealing: quorum-gated finality for resolved threads #1702

Description

@AaronGoldsmith

Problem

Conversations resolve, but the substrate has no way to say so. A decision thread reaches its conclusion and then accretes drive-by replies for weeks, each one reopening a question that was already answered. Clients improvise with ✅ emoji conventions — nothing enforces them, and nothing records who decided the matter was closed.

The gap is real in the current kind registry: kind:46030/46031 record a single approver's judgment and have no consequence outside workflow execution. There's no threshold semantic and no notion of finality anywhere in the tree (no VISION doc, NIP, or open issue mentions sealing or quorum — happy to be corrected).

Proposal (one paragraph)

Threads can be sealed: a kind:46040 seal request names a thread root, a threshold k, and an explicit approver set (relay-validated against channel membership, snapshotted at request time). Approvers publish individually signed kind:46041 approvals bound to the request id, so consent is bound to exact parameters. When the relay observes the k-th valid approval, it marks the thread sealed and emits a relay-signed, channel-scoped kind:8004 delta naming every counted approver — any client can re-verify the quorum from the signed approvals. Once enforced, the relay rejects new writes into the sealed reply tree (sealed: OK message). Unsealing is the same grammar in reverse, permanently exempt from the write-block (following the moderation Phase 1 precedent that restriction-lifting paths must never be blockable), with unseal quorum ≥ seal quorum and the unseal approver set drawn from the original approvers ∪ admins. Admin single-approver thread locking is the k=1 degenerate case, not a separate feature.

State model

OPEN ──(46040 accepted)──▶ OPEN + PENDING_SEAL
OPEN + PENDING_SEAL ──(k-th 46041 accepted)──▶ SEALED
OPEN + PENDING_SEAL ──(lapse/expiry)──▶ OPEN
SEALED ──(46043 accepted)──▶ SEALED + PENDING_UNSEAL
SEALED + PENDING_UNSEAL ──(k'-th 46041 accepted)──▶ OPEN
SEALED + PENDING_UNSEAL ──(lapse/expiry)──▶ SEALED

PENDING_UNSEAL is a sub-state of SEALED — the write-block never loosens while an unseal quorum accumulates. History is append-only; seal → unseal → seal cycles are legal and fully attributed.

Why this mechanism

Every team already trusts it daily: GitHub branch protection is relay-enforced quorum — individually attributed approvals, counted by the forge, gating an irreversible-ish action. Deliberately no threshold cryptography: a threshold signature proves that k participants consented while erasing which k, and the audit question is always "who authorized closing this?" — the answer must be names. The relay is trusted for enforcement, not for attribution: every counted approval is independently verifiable from its signature.

Precedents followed

  • Handler shape: structural sibling of handlers/identity_archive.rs (user-signed request → relay validation → state table → relay-signed deltas in the 8xxx range)
  • Write-block placement: adjacent to the ban/timeout gate in ingest, including its key edge case — the lifting path stays writable
  • Kinds at 46040–46043 in the workflow range; deltas carry h and follow the subject channel's read controls
  • Phased rollout with a numbered-decisions plan doc, mirroring Community Moderation Phase 1: Phase 1 records seals (state + deltas, no enforcement — "recorded, not enforced" per the draft's conformance section), Phase 2 enforces (the ingest write-block, with an explicit by-kind enforcement matrix keyed on resolved thread ancestry, not raw e tags)

Deliberately deferred

Channel/community sealing, role-scoped and predicate quorums (cardinality is framed as the minimal quorum predicate so richer predicates are an extension, not a redesign), a soft "checkpoint" tier, blocking objections (deny votes), and a transparency log. None are blocked or foreclosed by this design.

Status

Full NIP draft (wire format, validation rules, trust model, invariants, concurrency edge cases) and a design-decisions doc with anticipated Q&A are written and have been through several adversarial review passes — see gist below. If the approach has support, I'll open them as a docs-only RFC PR for detailed review before any implementation.

Related: #54 (these kinds extend the 46xxx approval vocabulary that workflow support established).

Feedback especially wanted on:

  1. Is thread-root the right first subject, or would maintainers rather see this land against a different scope?
  2. The Phase 1/Phase 2 split — is "record first, enforce second" the right rollout, or should enforcement ship in one PR?
  3. Kind number placement (46040–46043 + 8004/8005 following NIP-IA's delta convention) — any collisions with unpublished plans?
Attachments

Gist (both docs): https://gist.github.com/AaronGoldsmith/dbaf3382815b2f10f33b0ef9b0b34f5d

  • NIP-SL.md — full protocol draft
  • THREAD_SEALING_PLAN.md — design decisions + Q&A + phased checklist

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions