iroh-with-atp

Using AT Protocol Identity and Discovery for Iroh P2P Applications

published by on discuss this post



This document proposes a convention for binding AT Protocol (atproto) user identities to Iroh peer endpoints, so that socially-aware peer-to-peer applications can discover and authenticate each other without inventing a separate identity or trust layer.

This is an open request for coordination between any relevant or interested parties in the atproto and/or iroh ecosystems. The name iroh-with-atp is a working title.

Background and motivation

Iroh is a peer-to-peer networking toolkit. Its 1.0 release provides stable implementations in Rust, Python, and TypeScript. Iroh's core guarantee is:

You can reach a peer no matter where it is, and the connection you get is the best one available.

Every Iroh endpoint is identified by an Ed25519 public key known as a EndpointID. That key is the authority used to establish connections, regardless of the peer's network location. QUIC + TLS 1.3 provide end-to-end encryption, authentication, and stream multiplexing. Once connected, peers run specific protocols to transfer files, sync documents, broadcast messages, etc.

atproto is a decentralized social protocol. Identity is rooted in a DID (decentralized identifier, typically did:plc or did:web). A user's data lives in their repository, hosted by a personal data server (PDS). Repos are public, signed, and content-addressed. DRISL is the canonical record encoding, and Lexicon is the schema language.

This combination is attractive because:

  • atproto already provides stable user identity and public-key distribution/revocation.
  • Iroh already provides NAT traversal, peer addressing, and encrypted transport.
  • Many P2P applications want both, but neither system alone is optimized for the other.

Prior art includes:

Both use an atproto repo as an authority-published directory. iroh-with-atp applies the same pattern to a peer-to-peer rather than client/server topology.

Scope

iroh-with-atp defines:

  1. A mapping between an Iroh EndpointID and a did:key identifier.
  2. A Lexicon record type for device attestation: a signed binding from an atproto account to an Iroh node.
  3. A discovery procedure: given a signed-in user, how to find the active devices of people that user follows.
  4. A trust model: how to decide whether an attested device is safe to connect to.

iroh-with-atp does not define:

  • Application-level protocols run over Iroh once connections are established.
  • A replacement for PDS identity resolution or repo sync. atproto remains the source of truth for user ↔ device bindings.

Identity mapping

Iroh uses a 32-byte Ed25519 public key as its EndpointID. The DID Key specification is used to encode this key as a DID:

Iroh EndpointID ↔ did:key:z6Mk…

As per the DID Key specification, the did:key method-specific identifier is produced by taking the raw 32-byte Ed25519 public key, prefixing it with the Ed25519 multicodec code (0xed, varint), and encoding the result as base58-btc.

This gives every Iroh endpoint a stable, self-certifying identifier that is also portable and independently verifiable.

Device attestation

A device attestation is a record that binds a user to one of their Iroh nodes. The proposed record shape is:

{
  "$type": "example.protocol.device",
  "iss": "did:plc:abc…",
  "sub": "did:key:z6Mk…",
  "via": [
    "example.protocol.blob/0"
  ],
  "proof": {
    "$bytes": "…"
  }
}
  • iss: the DID of the user who claims this device.
  • sub: the did:key identifier derived from the Iroh EndpointID; the device being claimed.
  • via: the application identifiers (ALPNs) accepted by this device.
  • proof: the device key's own signature over the canonical DRISL encoding of the record without the proof field.

The record is authenticated in two independent ways:

  1. Repo-level attestation. The record is part of the user's signed repository. The commit published by the PDS proves the user authorized the record.
  2. Device-level attestation. The proof field proves the device whose EndpointID is named in sub has also endorsed the binding to the user named in iss.

The device signature is needed because the repo-level signature alone only proves that the user authorized the record. It alone does not prove that the device named in sub approves of the binding.

To verify the device signature, remove the proof field, canonicalize the remainder with DRISL, and verify the resulting bytes against the Ed25519 public key decoded from sub.

Replay resistance

The iss field is essential for security. Without it, a device signature would only prove that some device existed during the validity window, not that it accepted being bound to a specific user. By including iss in the signed payload, the device signature becomes a binding between a specific user and a specific node.

A client MUST reject an attestation if:

  • the repo from which it was fetched is not the repo of iss; or
  • the sub value is not a valid did:key encoding of an Ed25519 public key; or
  • the device signature in proof does not verify against the public key encoded in sub; or

This mutual binding prevents a device attestation issued for one user from being replayed inside another user's repository.

Discovery

Given a signed-in user, the client:

  1. Resolves the user's follow graph via app.bsky.graph.follow (a more generic method of finding the user's connections may be explored).
  2. For each followed DID, fetches active example.iroh.atp.device records from that user's repo.
  3. Verifies both repo and device signatures, plus the replay-resistance checks.
  4. Filters out invalid or malformed attestations.
  5. Builds an initial peer list from the remaining attestations.

A device attestation is considered active if and only if it is present within the user's repository.

Device attestations published by a user, in their public repository and/or permissioned repositories, are the source of truth for "what devices does this user currently claim." Iroh is then used to connect to those devices.

Peer discovery MUST treat the follow graph as a discovery source, not as an authorization decision. Applications MUST obtain explicit user consent before initiating connections.

Trust model

Device attestations published to a user's repo prove:

  • The user named in iss controls the repo that published the attestation.
  • The device named in sub holds the corresponding private key and accepted the binding.

They do not prove that the device is currently under the user's control at the moment of connection. Phishing, device theft, or key compromise can break that assumption.

iroh-with-atp therefore supports two trust modes:

  • Attestation-only trust. The client considers any device attested by a followed user as a candidate peer, relying on the repo and device signatures. This is the default and is sufficient for low-risk applications.
  • Manual out-of-band verification. For high-risk applications, the signed-in user may manually mark a specific device as trusted, similar to OMEMO key verification. This can be done via a side channel (QR code, in-person exchange, etc.) and is entirely application-defined.

Application layer

Once a peer list is established, applications run whatever Iroh protocols they want over the resulting connections. Iroh protocols are identified by ALPN strings. Designers of applications should pick stable, namespaced ALPN identifiers derived from their Lexicon authority (i.e. community.lexicon, com.germnetwork), followed by an optional version string separated by a /.

It is recommended that application messages use the same primitives atproto uses elsewhere: Lexicon for schema definition and validation, and DRISL as the canonical serialization. This keeps application data compatible with atproto tooling and lets records be hashed, signed, and archived in the same way.

Specific application subprotocols (file transfer, media streaming, live sync, gossip, etc.) are out of scope for iroh-with-atp.

Open questions

Governance and namespace

  • What namespace should own the device attestation Lexicon?
  • What should the working title iroh-with-atp be replaced with?

Security and lifecycle

  • How should a user announce revocation? Deleting the record is sufficient if peers consume repo deletion events, but is a more active revocation record desirable?
  • How many active devices should a user be allowed to attest? Should PDSes enforce this?

Discovery and permissioned data

  • How should device attestations be discovered in permissioned spaces? Should there be a standard permissioned record type, or is this strictly application-specific?
  • Should discovery require mutual follows, a permissioned record, or remain follow-graph-only?

Application conventions

  • Are there common Iroh protocol patterns (blobs, docs, gossip) that benefit from shared Lexicon wrappers?
  • Should the attestation include an optional aud field to scope the device binding to a specific application or set of protocols?