{"id":"6682ed5673b88e9135f64bd26ee2b029247f0ed50f1534fe66af851fc8f96047","pubkey":"da19f1cd34beca44be74da4b306d9d1dd86b6343cef94ce22c49c6f59816e5bd","created_at":1774618172,"kind":30817,"tags":[["title","NIP-APPROVAL: Multi-Party Approval Gates"],["k","30570"],["k","30571"],["d","nip-approval"]],"content":"NIP-APPROVAL\n============\n\nMulti-Party Approval Gates\n----------------------------\n\n`draft` `optional`\n\nTwo addressable event kinds for gating workflow progression on Nostr — a proposer publishes a gate requiring sign-off, and one or more designated reviewers respond with approval, rejection, or revision requests.\n\n> **Design principle:** Approval gates are a coordination primitive. They communicate that sign-off is required and record decisions — they do not enforce access control. Enforcement is the responsibility of the consuming application.\n\n> **Standalone.** This NIP works independently on any Nostr application.\n\n## Motivation\n\nNostr has events for publishing content, sending messages, and making payments, but no standard mechanism for **gating progress on a decision**. Many real-world workflows require explicit sign-off before the next step can proceed:\n\n- **Pull request reviews** — a maintainer must approve before merging\n- **Content moderation** — an editor must approve before publishing\n- **Grant applications** — a committee must approve before funding\n- **Procurement** — a budget holder must approve before purchasing\n- **Inspections** — a qualified inspector must sign off before work continues\n\nWithout a standard, every application invents its own approval scheme with incompatible tag conventions. NIP-APPROVAL provides a minimal, composable primitive that any Nostr application can adopt for structured decision-making.\n\n## Relationship to Existing NIPs\n\n- **NIP-25 (Reactions):** Reactions express lightweight sentiment (`+`/`-`) with no named reviewer set, no quorum or threshold semantics, no revision loop, and no deadline enforcement. A reaction does not encode who is required to approve; approval gates name the required reviewers upfront and track structured decisions (approved, rejected, revision requested) with mandatory reasoning.\n- **NIP-22 (Comments):** Comments carry unstructured text with no decision semantics, no gate status tracking, and no distinction between \"I have thoughts\" and \"I formally approve.\" Approval responses are structured decisions, not discussion.\n\n## Kinds\n\n| kind  | description        |\n| ----- | ------------------ |\n| 30570 | Approval Gate      |\n| 30571 | Approval Response  |\n\nBoth kinds are addressable events (NIP-01). The `d` tag format ensures each event occupies a unique slot, allowing updates via republication.\n\n---\n\n## Approval Gate (`kind:30570`)\n\nPublished by a proposer to create a gate requiring one or more reviewers to sign off. Addressable — the proposer can update the proposal before a final decision is recorded.\n\n```json\n{\n    \"kind\": 30570,\n    \"pubkey\": \"<proposer-hex-pubkey>\",\n    \"created_at\": 1698765000,\n    \"tags\": [\n        [\"d\", \"pr_review_42:gate:code_review\"],\n        [\"t\", \"approval-gate\"],\n        [\"alt\", \"Approval gate: code review for PR #42\"],\n        [\"gate_type\", \"review\"],\n        [\"gate_authority\", \"<reviewer-hex-pubkey>\"],\n        [\"gate_status\", \"pending\"],\n        [\"expiration\", \"1699370000\"]\n    ],\n    \"content\": \"PR #42 ready for review. Implements NIP-APPROVAL event kinds.\",\n    \"id\": \"<32-bytes lowercase hex>\",\n    \"sig\": \"<64-bytes lowercase hex>\"\n}\n```\n\nTags:\n\n* `d` (REQUIRED): Addressable event identifier. RECOMMENDED format: `<context_id>:gate:<sequence>`. Applications MAY use any d-tag format that ensures uniqueness.\n* `t` (RECOMMENDED): Protocol family marker. RECOMMENDED value: `\"approval-gate\"`.\n* `gate_type` (REQUIRED): Type of gate. Primary values: `review`, `approval`. Applications MAY define additional gate types such as `regulatory`, `inspection`, or other domain-specific values.\n* `gate_authority` (REQUIRED, one or more): Hex pubkey of a required reviewer. Multiple `gate_authority` tags indicate that all listed reviewers must respond (see Multi-Reviewer Gates below).\n* `gate_status` (REQUIRED): MUST be `\"pending\"` on creation.\n* `expiration` (RECOMMENDED): Unix timestamp — deadline for the review. Clients SHOULD use NIP-40 `expiration` for relay-level enforcement.\n* `p` (RECOMMENDED): Additional parties to notify.\n* `gate_reference` (OPTIONAL): External reference (certificate number, permit ID, PR URL).\n* `ref` (OPTIONAL): Cross-application external reference.\n* `e` (OPTIONAL): Event ID of the event being gated (e.g. the content draft, the pull request event).\n\n**Content:** Plain text or NIP-44 encrypted JSON describing what requires approval. May include specification references, inspection criteria, or submission details.\n\n---\n\n## Approval Response (`kind:30571`)\n\nPublished by a reviewer to approve, reject, or request revision of a gated proposal. The `d` tag format allows one response per reviewer per gate.\n\n```json\n{\n    \"kind\": 30571,\n    \"pubkey\": \"<reviewer-hex-pubkey>\",\n    \"created_at\": 1698766000,\n    \"tags\": [\n        [\"d\", \"pr_review_42:gate:code_review:response:<reviewer-hex-pubkey>\"],\n        [\"t\", \"approval-response\"],\n        [\"alt\", \"Approval response: approved for code review\"],\n        [\"e\", \"<gate-event-id>\", \"wss://relay.example.com\"],\n        [\"decision\", \"approved\"],\n        [\"p\", \"<proposer-hex-pubkey>\"],\n        [\"gate_reference\", \"REVIEW-2026-0042\"]\n    ],\n    \"content\": \"Code looks good. Approved with no changes required.\",\n    \"id\": \"<32-bytes lowercase hex>\",\n    \"sig\": \"<64-bytes lowercase hex>\"\n}\n```\n\nTags:\n\n* `d` (REQUIRED): Format `<gate_d_tag>:response:<reviewer_pubkey>`. One response per reviewer per gate.\n* `t` (REQUIRED): Protocol family marker. MUST be `\"approval-response\"`.\n* `e` (REQUIRED): Event ID of the Kind 30570 gate being responded to.\n* `decision` (REQUIRED): The reviewer's decision. One of `\"approved\"`, `\"rejected\"`, or `\"revise\"`.\n* `p` (RECOMMENDED): Proposer's pubkey (for notification).\n* `gate_reference` (OPTIONAL): External reference (inspection report number, review ID).\n* `revision_notes` (OPTIONAL): Feedback when decision is `\"revise\"`.\n\n**Content:** Plain text or NIP-44 encrypted JSON with the reviewer's notes, findings, or conditions.\n\n---\n\n## Protocol Flow\n\n```\n  Proposer                       Relay                     Reviewer(s)\n      |                            |                            |\n      |-- kind:30570 Gate -------->|                            |\n      |  (gate_status: pending)    |                            |\n      |                            |------- notification ------>|\n      |                            |                            |\n      |                            |<-- kind:30571 Response ----|\n      |                            |    (decision: revise)      |\n      |<------ notification -------|                            |\n      |                            |                            |\n      |-- kind:30570 Gate -------->|  (updated proposal)        |\n      |                            |------- notification ------>|\n      |                            |                            |\n      |                            |<-- kind:30571 Response ----|\n      |                            |    (decision: approved)    |\n      |<------ notification -------|                            |\n      |                            |                            |\n      |  Proceed to next phase     |                            |\n```\n\n1. **Gate:** Proposer publishes `kind:30570` with `gate_status: pending` and one or more `gate_authority` tags identifying the required reviewers.\n2. **Review:** Each reviewer evaluates the proposal and publishes `kind:30571` with their `decision`.\n3. **Revision (optional):** If a reviewer requests revision (`decision: revise`), the proposer updates their `kind:30570` event with revisions, and the reviewer evaluates again.\n4. **Resolution:** The gate is resolved when all required reviewers have published a final `approved` or `rejected` response. If any reviewer rejects, the gate is rejected.\n\nThe following diagram illustrates the gate state transitions:\n\n\n![Approval Gate State Transitions](https://raw.githubusercontent.com/forgesworn/nip-drafts/main/images/approval-1.png)\n\n```mermaid\nflowchart TD\n    classDef green fill:#1b3d2d,stroke:#16c79a,color:#f0f0f0\n    classDef yellow fill:#2d2d1b,stroke:#f5a623,color:#f0f0f0\n    classDef blue fill:#1b2d3d,stroke:#0f3460,color:#f0f0f0\n    classDef red fill:#3d1b1b,stroke:#e94560,color:#f0f0f0\n\n    CREATE([Proposer publishes<br/>kind:30570 Gate<br/>gate_status: pending]):::blue\n\n    CREATE --> PENDING\n\n    PENDING[PENDING<br/>Awaiting reviewer responses]:::yellow\n\n    PENDING --> RESPONSE{Reviewer publishes<br/>kind:30571 Response}:::blue\n\n    RESPONSE -- \"decision: revise\" --> REVISION[REVISION REQUESTED<br/>Proposer updates<br/>kind:30570 with changes]:::yellow\n    REVISION --> PENDING\n\n    RESPONSE -- \"decision: rejected<br/>(any reviewer)\" --> REJECTED([REJECTED]):::red\n\n    RESPONSE -- \"decision: approved\" --> CHECK{All gate_authority<br/>reviewers approved?}:::yellow\n\n    CHECK -- \"No, still waiting<br/>on other reviewers\" --> PENDING\n    CHECK -- \"Yes, all approved\" --> APPROVED([APPROVED<br/>Proceed to next phase]):::green\n\n    PENDING -- \"expiration reached<br/>without resolution\" --> EXPIRED([EXPIRED]):::red\n```\n\n## Multi-Reviewer Gates\n\nWhen multiple reviewers must approve, the proposer publishes multiple `gate_authority` tags on the Kind 30570 event. Each reviewer publishes their own Kind 30571 response. The gate is considered approved only when **all** listed authorities have published `approved` responses. If any reviewer publishes `rejected`, the gate is rejected. Clients SHOULD track the set of outstanding approvals and display progress.\n\n## Use Cases\n\n### Code Review & Merge Gating\n\nA Nostr-native Git collaboration tool can use approval gates to model pull request reviews. The proposer creates a `kind:30570` gate referencing the PR, with the maintainer's pubkey as `gate_authority`. The maintainer reviews and publishes `kind:30571` with their decision. CI/CD systems can subscribe to approval responses to trigger automated merges.\n\n### Content Moderation & Editorial Workflow\n\nA Nostr publishing platform can gate article publication behind editorial approval. Authors submit drafts as `kind:30570` gates with `gate_type: review`. Editors respond with `kind:30571` — approving for publication, rejecting, or requesting revisions. The revision flow allows iterative editing before final sign-off.\n\n### Grant & Funding Applications\n\nA decentralized grant program can use approval gates for application review. Applicants publish `kind:30570` with the grant committee members as `gate_authority` tags. Committee members independently review and vote. The multi-reviewer gate model ensures all required approvals are recorded before funds are released.\n\n### Regulatory & Compliance Sign-offs\n\nAny application requiring regulatory approval (building permits, food safety certificates, financial compliance checks) can use approval gates to record the decision. The `gate_reference` tag links to external regulatory identifiers, creating an auditable trail on Nostr.\n\n## Test Vectors\n\nAll examples use timestamps around `1709280000` (2024-03-01) and placeholder hex pubkeys.\n\n### Kind 30570 — Approval Gate\n\nA regulatory inspection gate requiring sign-off from 2 authorities before work can proceed.\n\n```json\n{\n  \"kind\": 30570,\n  \"pubkey\": \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\",\n  \"created_at\": 1709280000,\n  \"tags\": [\n    [\"d\", \"site_inspection_007:gate:structural_review\"],\n    [\"t\", \"approval-gate\"],\n    [\"alt\", \"Approval gate: structural inspection requiring two reviewers\"],\n    [\"gate_type\", \"inspection\"],\n    [\"gate_authority\", \"b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3\"],\n    [\"gate_authority\", \"c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4\"],\n    [\"gate_status\", \"pending\"],\n    [\"expiration\", \"1709366400\"],\n    [\"gate_reference\", \"INSP-2024-0307\"]\n  ],\n  \"content\": \"Structural inspection required before phase 2 construction can begin. Both inspectors must sign off.\",\n  \"id\": \"<32-byte-hex>\",\n  \"sig\": \"<64-byte-hex>\"\n}\n```\n\n### Kind 30571 — Approval Response\n\nAn \"approved\" response from one of the gate authorities.\n\n```json\n{\n  \"kind\": 30571,\n  \"pubkey\": \"b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3\",\n  \"created_at\": 1709283600,\n  \"tags\": [\n    [\"d\", \"site_inspection_007:gate:structural_review:response:b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3\"],\n    [\"t\", \"approval-response\"],\n    [\"alt\", \"Approval response: structural inspection approved\"],\n    [\"e\", \"aaaa1111bbbb2222cccc3333dddd4444eeee5555ffff6666aaaa1111bbbb2222\", \"wss://relay.example.com\"],\n    [\"decision\", \"approved\"],\n    [\"p\", \"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\"],\n    [\"gate_reference\", \"INSP-2024-0307-A\"]\n  ],\n  \"content\": \"Structural integrity confirmed. Foundation and load-bearing walls meet specification.\",\n  \"id\": \"<32-byte-hex>\",\n  \"sig\": \"<64-byte-hex>\"\n}\n```\n\n## Security Considerations\n\n* **Authority verification.** Implementations MUST verify that Kind 30571 responses are signed by a pubkey listed in the corresponding Kind 30570's `gate_authority` tags. Responses from unauthorised pubkeys MUST be ignored.\n* **Replay protection.** The addressable `d` tag format (one response per reviewer per gate) prevents duplicate approvals. Relays SHOULD store only the latest response from each reviewer.\n* **Expiration enforcement.** Gates with an `expiration` tag SHOULD be considered expired after the deadline. Clients MUST NOT accept approval responses published after the gate's expiration timestamp.\n* **Content encryption.** When gate content is sensitive (e.g. financial details, personal information), the `content` field SHOULD be NIP-44 encrypted to the gate authority and proposer.\n* **Immutability after decision.** Once a reviewer publishes an `approved` or `rejected` response, the proposer SHOULD NOT update the Kind 30570 event. Clients MAY warn if the gate content changes after a final decision has been recorded.\n\n## Dependencies\n\n* [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md): Basic protocol flow, addressable events\n* [NIP-40](https://github.com/nostr-protocol/nips/blob/master/40.md): Expiration timestamps (gate deadlines)\n* [NIP-44](https://github.com/nostr-protocol/nips/blob/master/44.md): Versioned encrypted payloads (sensitive gate content)\n\n## Reference Implementation\n\nNo public reference implementation exists yet. Implementors SHOULD refer to the kind definitions above.\n\nA minimal implementation requires:\n\n1. A Nostr client that supports addressable event publishing.\n2. Logic to track gate state by aggregating Kind 30571 responses per Kind 30570 gate.\n3. Authority verification to ensure only designated reviewers' responses are counted.","sig":"b1c45ed8ca293e213fb2daab346289cc35068d41e85e16258775637fb061a0e64aabcfd22bdbd21f689d27d48ed08f9238d5498fa8b59e395ea86ca4adfa6df9"}