there's just a silly shakespeare export of the ui. i need to make more of a "nip" repo now that i'm seeing the shape of it better. i'll post it when its ready.

Reply to this note

Please Login to reply.

Discussion

## 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-`.

Awesome thanks! Would love to implement this directly into Bloom so might have some ideas to throw your way.

Ooh very interesting... What purpose would agreements serve in bloom? Or are you thinking of the general idea of mutually signing , like a file..?

Yeah cosigning PDFs, things like thqt

cool yea that's great! i didn't exhaustively search the nips first to see if there is an existing "two people sign the same hash" thing anywhere - for all i know there is something even better out there. let me know how it goes!

(...and now that i'm thinking of it, any note id is already a hash of its content, so one of my steps is sort of unnecessary..)