Replies (1)

## Overview Attestr is a protocol built on Nostr that allows **two parties to mutually sign a statement or promise**, timestamp it, and later attest whether it was honored. It uses existing Nostr primitives (events, tags, signatures) and optionally integrates OpenTimestamps for third-party timestamping. ## Event Kinds | Kind | Name | Description | | ------- | -------------------- | ---------------------------------------------- | | `18973` | Draft Version | Replaceable drafts during negotiation | | `30677` | Agreement Commitment | Each party signs the agreed hash | | `35319` | Outcome Attestation | Each party marks "honored" or "disputed" | ## Event Structure ### Draft Version (`kind: 18973`) Used during negotiation phase. Replaceable events that contain draft contract text. Each party can update their draft by publishing a new event with the same UUID, with only the latest version from each party being stored. **Required tags:** - `["d", "<uuid>"]` - Contract UUID - `["p", "<counterparty-pubkey>"]` - Counterparty public key **Optional tags:** - `["attestr-conclusion-date", "<unix-timestamp>"]` - Optional maturity date as UNIX timestamp (string) - `["alt", "Attestr contract draft"]` - Human-readable description **Content:** Draft contract text **Behavior:** Since these are replaceable events (kind 18973), only the latest draft from each party is stored by relays. When a party updates their draft, it replaces their previous version. This keeps the negotiation focused on current positions without cluttering storage with outdated proposals. **Note:** We use `attestr-conclusion-date` instead of the standard `expiration` tag to avoid conflicts with NIP-40, which would cause relays to automatically reject events past their expiration time. ### Agreement Commitment (`kind: 30677`) Signed commitment to the final agreed contract hash. **Required tags:** - `["d", "<uuid>"]` - Contract UUID - `["sha256", "<hash>"]` - SHA-256 hash of final contract text - `["p", "<counterparty-pubkey>"]` - Counterparty public key **Optional tags:** - `["attestr-conclusion-date", "<unix-timestamp>"]` - Optional maturity date as UNIX timestamp - `["ots", "<base64>"]` - OpenTimestamps proof - `["alt", "Attestr agreement commitment"]` - Human-readable description **Content:** `agreement_hash=<hash>` ### Outcome Attestation (`kind: 35319`) Attestation of whether the contract was honored or disputed. **Required tags:** - `["d", "<uuid>"]` - Contract UUID - `["sha256", "<hash>"]` - SHA-256 hash of original contract - `["p", "<counterparty-pubkey>"]` - Counterparty public key **Optional tags:** - `["alt", "Attestr outcome attestation"]` - Human-readable description **Content:** `"honored"` or `"disputed"` ## Contract Lifecycle 1. **Negotiation:** Each party posts replaceable drafts (`18973`) until final text is agreed - Parties can update their drafts by publishing new events with the same UUID - Only the latest draft from each party is stored (replaceable behavior) - Negotiation continues until both parties are satisfied with the current terms 2. **Commitment:** Both parties post `30677` signing the same `agreement_hash` - Once committed, drafts can no longer be edited - Both parties must sign the exact same contract text hash 3. **Outcome:** After maturity, parties post `35319` with `"honored"` or `"disputed"` ## Agreement Hash Computation The agreement hash is computed as SHA-256 of the normalized contract text: ```javascript function computeAgreementHash(contractText) { const normalized = contractText.trim().replace(/\r\n/g, '\n'); return sha256(normalized); } ``` ## Verification A contract is considered mutually agreed when: 1. Two distinct pubkeys have published `30677` events 2. Both events have the same UUID (`d` tag) 3. Both events have the same agreement hash (`sha256` tag) ## Contract UUID Contract UUIDs should be globally unique identifiers, typically in the format `attestr-<uuid-v4>`.