## 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", ""]` - Contract UUID

- `["p", ""]` - Counterparty public key

**Optional tags:**

- `["attestr-conclusion-date", ""]` - 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", ""]` - Contract UUID

- `["sha256", ""]` - SHA-256 hash of final contract text

- `["p", ""]` - Counterparty public key

**Optional tags:**

- `["attestr-conclusion-date", ""]` - Optional maturity date as UNIX timestamp

- `["ots", ""]` - OpenTimestamps proof

- `["alt", "Attestr agreement commitment"]` - Human-readable description

**Content:** `agreement_hash=`

### Outcome Attestation (`kind: 35319`)

Attestation of whether the contract was honored or disputed.

**Required tags:**

- `["d", ""]` - Contract UUID

- `["sha256", ""]` - SHA-256 hash of original contract

- `["p", ""]` - 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-`.

Reply to this note

Please Login to reply.

Discussion

No replies yet.