Software Distribution on ATProto: A Homebrew Tap Backed by Lexicon Records

With recent updates to the atproto-crates, wanted to start distributing the tools in that repository using home-brew. That got me thinking that the application and release information should be on-protocol.

Lexicons

@knowtheory.net and I were riffing on some lexicon structure, so I used https://lexicon.garden/ to create some lexicons that generally resemble an application and a release with artifacts. That fit my use case because I want to support multiple applications that are built for multiple operating systems and architectures.

Lexicon Garden assigned the garden.lexicon.exultant-zebra NSID, so I just used that. This is what they look like:

In this early PoC, I went with tags instead of forcing a taxonomy. One artifact may be an arm64 macOS binary, and another may be a source code tarball.

Here’s the live app record for atpcid:

{
  "$type": "garden.lexicon.exultant-zebra.app",
  "name": "atpcid",
  "description": "A CLI tool for generating ATProtocol CIDs",
  "distributions": [
    {
      "uri": "at://did:plc:cbkjy5n7bk3ax2wplmtjofq2/garden.lexicon.exultant-zebra.distribution/3mffgfbwq5l2i",
      "cid": "bafyreig22ce5yzyuvrbijgjeqtry67pel7qqxakareo7fqht7ablatqznq"
    }
  ]
}

And its distribution record, which covers four platform targets:

{
  "$type": "garden.lexicon.exultant-zebra.distribution",
  "version": "0.14.0",
  "artifacts": [
    {
      "tags": ["arm64", "darwin"],
      "download": {
        "$type": "blob",
        "ref": { "$link": "bafkreihbjqacm3uwccneyulxlyxzh2hnmv6uxgmdwklyr3k5okjakphlzm" },
        "mimeType": "*/*",
        "size": 1095984
      }
    },
    ...
  ]
}

The app record holds strong refs to distributions, pinning both the AT-URI and CID, so there are strong guarantees that data hasn’t been tampered with at rest or in transit.

Homebrew Tap

The homebrew tap at ngerakines/homebrew-tap uses these records to distribute atpcid (a CID resolver for ATProto) via brew install.

The formula works in two steps:

  1. It fetches the distribution record from the PDS via com.atproto.repo.getRecord, pinned to a specific record CID.
  2. It matches the current platform’s tags against the distribution’s artifacts to find the right blob, then derives the download URL and SHA-256 checksum from the blob CID.

Homebrew uses sha256 checksums to verify the integrity of retrieved files which is great because ATProtocol also uses sha256 checksums for records and blobs in the form of CIDs.

Read more about CIDs at CIDs: What You Need to Know and Why, Part 1 - Nick's Blog.

Using the CIDs in strong refs just requires a a little bit of decoding and encoding to go from base32 to hex. No hardcoded URLs or checksums in the formula itself.

To release a new version, you publish an updated distribution record to the PDS with new artifact blobs, then update two constants in the formula: RECORD_CID and RKEY. Everything else (version string, download URLs, checksums for all platforms) is derived automatically from the record.

Try it out

You can install it now with:

brew install ngerakines/tap/atpcid

And then test the atpcid application yourself:

$ jo text="hello world" | atpcid --
bafyreig32cohooqm5w2f7pilujssreqw3p3i5wud4q2j5jvgll6wo5djta

ATProtocol’s content-addressing gives you integrity verification for free. The distribution record is the single source of truth, and pinning its CID makes the formula deterministic. It’s a small example, but it shows that ATProtocol’s data model works well beyond social networking.

Roadmap

This is an early proof of concept. It doesn’t include two important things that make this scale beyond a single tap:

  1. The identity must be resolved to get their PDS at install time. The formula constants should include the handle as well as the distribution record key, cid, and expected version.

  2. It should use com.atproto.sync.getRecord to get the CAR slice and verify the signature on it using verification method from the resolved DID document.

7 Likes