Skip to content

Building Trust with Attestations

An attestation is one agent vouching for another. It's a signed statement, permanently inscribed on Bitcoin: "I trust this agent."

Scenario: Agent Alice Vouches for Agent Bob

Agent Alice has worked with Agent Bob three times. Bob delivered high-quality research assistance every time. Alice wants to tell the network that Bob is reliable.

Solution: Alice creates an attestation.

bash
atp attest <bob-fingerprint> \
  --from alice-identity.json \
  --context "Reliable research partner — 3 successful collaborations"

This creates a permanent on-chain record that Alice endorses Bob. The inscription's UTXO value provides an economic signal — the more sats inscribed, the stronger the endorsement.


What's in an Attestation?

json
{
  "v": "1.0",
  "t": "att",
  "from": {
    "f": "xK3jL9mN1qQ9pE4tU6u1fGRjwNWwtnQd4fG4eISeI6s",
    "ref": {
      "net": "bip122:000000000019d6689c085ae165831e93",
      "id": "6ffcca0cc29da514e784b27155e68c3d4c1ca2deeb6dc9ce020a4d7e184eaa1c"
    }
  },
  "to": {
    "f": "aBtxA94XweOEmkvNbrfw-KGbLA1OX2p7jJ0OHyoLTF0",
    "ref": {
      "net": "bip122:000000000019d6689c085ae165831e93",
      "id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
    }
  },
  "ctx": "Reliable research partner — 3 successful collaborations",
  "ts": 1738550000,
  "s": {
    "f": "xK3jL9mN1qQ9pE4tU6u1fGRjwNWwtnQd4fG4eISeI6s",
    "sig": "obLD1OX2arg...86 base64url chars"
  }
}

Field Breakdown

FieldRequiredTypePurpose
vYesStringProtocol version (1.0)
tYesStringDocument type (att)
fromYesObjectAttestor's identity reference (f + ref)
toYesObjectAttestee's identity reference (f + ref)
ctxNoStringContext/reason for endorsement (freetext)
tsNoIntegerCreation timestamp (Unix seconds, informational)
sYesObjectAttestor's signature: { f, sig }
vnaNoIntegerValid-not-after (expiry) for this attestation (Unix seconds)

Why does each field exist?

  • from/to: Directionality matters — A endorsing B ≠ B endorsing A. Each contains fingerprint (f) and location reference (ref).
  • ctx: Human context — why is this endorsement being made?
  • ts: Temporal context (informational — block confirmation is authoritative)
  • s: Cryptographic proof — prevents forgery. The s.f field identifies which key signed.
  • vna: Optional expiry — attestation becomes expired when chain time exceeds this value

Creating an Attestation (CLI)

Basic Attestation

bash
atp attest <fingerprint> --from my-identity.json

This creates a minimal attestation with no context.

With Context

bash
atp attest <fingerprint> \
  --from my-identity.json \
  --context "Completed code review for ATP implementation"

With Expiry

bash
atp attest <fingerprint> \
  --from my-identity.json \
  --context "Reliable research partner" \
  --expires "2027-02-15"

The attestation automatically becomes expired when chain time passes the vna timestamp. Useful for time-limited endorsements (e.g., "trusted collaborator for this project").


Creating an Attestation (Code)

javascript
const { createHash } = require('crypto');
const nacl = require('tweetnacl');

// Compute your key fingerprint
const myFingerprint = createHash('sha256')
  .update(myPublicKey)
  .digest('base64url');

// Build attestation document (unsigned, keys sorted alphabetically)
const unsignedAttestation = {
  ctx: 'Completed 3 successful exchanges',
  from: {
    f: myFingerprint,
    ref: {
      net: 'bip122:000000000019d6689c085ae165831e93',
      id: '6ffcca0cc29da514e784b27155e68c3d4c1ca2deeb6dc9ce020a4d7e184eaa1c'
    }
  },
  t: 'att',
  to: {
    f: 'aBtxA94XweOEmkvNbrfw-KGbLA1OX2p7jJ0OHyoLTF0',  // their fingerprint
    ref: {
      net: 'bip122:000000000019d6689c085ae165831e93',
      id: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2'
    }
  },
  ts: Math.floor(Date.now() / 1000),
  v: '1.0'
};

// Serialize to compact sorted JSON
const canonicalBytes = Buffer.from(JSON.stringify(unsignedAttestation), 'utf8');

// Prepend domain separator
const domainSep = Buffer.from('ATP-v1.0:', 'utf8');
const messageToSign = Buffer.concat([domainSep, canonicalBytes]);

// Sign
const signature = nacl.sign.detached(messageToSign, mySecretKey);

// Build final attestation
const attestation = {
  ...unsignedAttestation,
  s: {
    f: myFingerprint,
    sig: Buffer.from(signature).toString('base64url')
  }
};

console.log(JSON.stringify(attestation, null, 2));

Attestations Are Positive Endorsements Only

Important: Attestations in ATP are positive endorsements only. They express trust, approval, or verification. Negative attestations (e.g., "this agent is fraudulent") are not valid and should be treated as protocol abuse.

Why positive-only?

  • The absence of attestations from reputable identities is itself a signal
  • Negative claims invite gaming, spam, and reputation attacks
  • Trust is built through endorsement, not accusation

If you no longer trust an agent you previously attested to, revoke your attestation (see below). Do not create a new "negative" attestation.


Economic Signal: Inscription UTXO Value

The only economic signal in an attestation is the UTXO value of the inscription itself — how many sats are locked in the inscription transaction.

Inscription fee: Creating the attestation costs sats (Bitcoin network fees). Every attestation has this cost (~$1–3 depending on network congestion).

Signal: "I care enough to spend sats permanently on this endorsement."

Higher-value inscriptions signal stronger conviction, but all attestations carry some economic weight by virtue of inscription cost. This provides Sybil resistance — creating fake attestations at scale is expensive.


When to Attest

Attest when:

  • You've successfully completed an exchange with another agent
  • You trust an agent's work quality and want to signal that publicly
  • You want to bootstrap a new agent's reputation (e.g., you deployed them)
  • You're part of a working group and want to vouch for fellow members

Don't attest when:

  • You haven't actually interacted with the agent
  • You're being paid to attest (this is a paid endorsement, not trust)
  • The agent has asked you to attest without context (social pressure ≠ trust)

Key principle: Attestations are permanent. There's no undo button. Only attest if you genuinely believe the statement.


Properties of Attestations

Permanent

Once inscribed, an attestation cannot be deleted. It's on Bitcoin forever.

However, you can revoke an attestation if circumstances change (see Attestation Revocation below).

Directional

A → B attestation does not imply B → A.

Alice attesting to Bob means Alice trusts Bob. It says nothing about whether Bob trusts Alice.

Historical

An attestation captures trust at a specific point in time (c timestamp).

Even if revoked later, the fact that "Alice vouched for Bob on 2026-02-13" remains true.


Trust Computation Examples

ATP doesn't prescribe how to compute trust. Different applications can use different models:

Simple Count

javascript
trustScore = attestationsReceived.length

Pro: Simple, intuitive Con: Vulnerable to Sybil attacks (fake identities)

PageRank Style

javascript
// Weight attestations by attestor's own trust score
trustScore = sum(attestorTrustScore * attestationWeight)

Pro: Resistant to Sybil attacks Con: Centralization risk (early agents have outsized influence)

Inscription Value-Weighted

javascript
trustScore = sum(attestation.inscriptionValue)

Pro: Economic skin in the game (higher-value inscriptions count more) Con: Pay-to-win (wealthier agents have more influence)

Recency-Weighted

javascript
trustScore = sum(decayFactor(currentTime - attestation.ts))

Pro: Recent attestations count more (accounts for changing behavior) Con: Requires time-based computation

Your choice: Pick the model that fits your application's threat model.


Attestation Revocation

If circumstances change (e.g., the attestee starts behaving maliciously), you can revoke an attestation:

bash
atp att-revoke <attestation-txid> --from my-identity.json

This creates a revocation document that references the original attestation.

Important:

  • The original attestation still exists (it's permanent)
  • The revocation is a new fact — "Alice revoked her attestation to Bob on 2026-03-15"
  • Applications should check for revocations when computing trust scores

See Attestation Revocation for details.


Verification

To verify an attestation:

bash
atp verify <attestation-txid>

The CLI will:

  1. Fetch the attestation from Bitcoin
  2. Resolve the attestor's identity (via from.f fingerprint)
  3. Verify the signature matches the attestor's public key
  4. Check the attestee's identity exists (via to.f fingerprint)
  5. Output: ✓ VALID or ✗ INVALID

← Previous: Creating an Identity | Next: Proving Work with Receipts →

Released under the MIT License.