Skip to content
Warning:Contents are available for feedback and preview; practices and lexicons can still change. Implement at your own risk!

Artifacts are files and metadata associated with a release; artifact manifests are a snapshot of those artifacts at a given time.

Responsibility

The primary goal of a manifest is to describe "outputs"—deliverables that release consumers can use. Optionally, a manifest can also describe "inputs"—the simplest example in software engineering is a git tag and/or revision from which these assets were created.

The important thing to remember is that release.software imposes no requirements and makes no assumptions of what you are releasing or what your release is derived from. Manifests serve as a vehicle for you to tie those elements together, in formats of your choice.

Immutability

Manifests are intended to be written and never updated. This is to preserve the implicit assumption that a version will always ship the same files. If you are building an appview consumer of release.software records, then we recommend that you treat update operations as a probable-compromise signal.

rkey—why TID?

Note that the record key of a software.release.manifest record is not a version string, as one might expect. Instead, they are TIDs. This is to enable producing software.release.version records that promote a manifest from one version to another.

Let's say you publish v1.0.0-rc, a release-candidate version that you want to test before officially announcing. Once that version passes your testing, you can publish v1.0.0 pointing at the same artifact manifest. This ensures that your versions share the exact same artifacts, without requiring a rebuild.

Artifact storage

While you have complete flexibility as to where you store remote artifacts, we provide two lexicon definitions that should cover the two most-common storage mediums: blobs and HTTP.

Blobs

Blobs are likely the most convenient storage medium for your artifacts. Protocol-level support and CIDs offer a lot of convenience and security, especially for the purposes of delivering files. You can read more about blobs and ATProto in the official spec.

release.software provides a lexicon type software.release.defs#blobArtifact that you can use directly in the software.release.manifest#artifacts array itself, or embedded in your own lexicon:

software.release.defs#blobArtifact
{
  "type": "object",
  "description": "A named downloadable file stored as a blob in the manifest publisher's PDS. Consumers MUST verify downloaded bytes against the blob CID. Target metadata is optional and describes coarse runtime compatibility, not installation instructions.",
  "required": [
    "fileName",
    "blob"
  ],
  "properties": {
    "fileName": {
      "type": "string",
      "minLength": 1,
      "description": "Suggested download filename, not a path. MUST NOT contain slashes, backslashes, or control characters, and MUST NOT be dot or dot-dot. Consumers MUST handle it as untrusted input and ensure it is safe for the destination filesystem. Filenames do not define target compatibility.",
      "maxLength": 255
    },
    "blob": {
      "type": "blob",
      "accept": [
        "*/*"
      ],
      "description": "The artifact bytes. The blob reference supplies the content CID, MIME type, and size. No artifact-specific size limit is imposed; hosting services may impose their own limits."
    },
    "target": {
      "type": "ref",
      "ref": "#target",
      "description": "Intended runtime target. Omission means compatibility is unspecified, not universal."
    },
    "metadata": {
      "type": "array",
      "items": {
        "type": "union",
        "refs": [],
        "closed": false
      },
      "description": "Typed ecosystem-specific metadata fragments. Metadata supplements core artifact fields and MUST NOT override or contradict them. Consumers may display or offer downloads of artifacts with unknown fragment types, but MUST NOT assume those fragments impose no additional requirements for automated installation. Unknown fragment types do not establish compatibility or suitability for automated installation."
    }
  }
}
fileNamestringRequiredminLength:1maxLength:255
Suggested download filename, not a path. MUST NOT contain slashes, backslashes, or control characters, and MUST NOT be dot or dot-dot. Consumers MUST handle it as untrusted input and ensure it is safe for the destination filesystem. Filenames do not define target compatibility.
blobblobRequiredaccept:*/*
The artifact bytes. The blob reference supplies the content CID, MIME type, and size. No artifact-specific size limit is imposed; hosting services may impose their own limits.
targetrefOptionalref:#target
Intended runtime target. Omission means compatibility is unspecified, not universal.
metadataarrayOptional
Typed ecosystem-specific metadata fragments. Metadata supplements core artifact fields and MUST NOT override or contradict them. Consumers may display or offer downloads of artifacts with unknown fragment types, but MUST NOT assume those fragments impose no additional requirements for automated installation. Unknown fragment types do not establish compatibility or suitability for automated installation.

Verification

A CID is not only an identifier: It also functions as a checksum. After fetching a blob artifact, the artifact's consumer should ALWAYS re-compute the file's CID and verify that it matches the one used to fetch the blob. Details on CID construction are available in the ATProto docs.

Limitations

Issues with blob storage are largely a result of anti-abuse measures. Most PDS instances impose a maximum size on blob uploads; some inspect ("sniff") blob contents and reject uploads as a moderation action. While you are not guaranteed to run into these issues, publishers should check their hosting provider's upload limits and content policies.

HTTP

HTTP storage is a reasonable fallback if you cannot store your artifact as a blob. The httpArtifact fragment requires an HTTPS download URL. The tradeoff is that you will need to juggle an external service—likely a public S3-compatible bucket on a hosting provider of your choice.

software.release.defs#httpArtifact
{
  "type": "object",
  "description": "A named downloadable file hosted at an external HTTPS URL. The URL locates the file; the CID identifies its bytes. Consumers MUST verify both CID and size before executing, installing, or extracting the file. Consumers fetching URLs MUST apply appropriate network access restrictions, including to redirects.",
  "required": [
    "fileName",
    "url",
    "cid",
    "size"
  ],
  "properties": {
    "fileName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "description": "Suggested download filename, not a path. MUST NOT contain slashes, backslashes, or control characters, and MUST NOT be dot or dot-dot. Consumers MUST handle it as untrusted input and ensure it is safe for the destination filesystem. Filenames do not define target compatibility."
    },
    "url": {
      "type": "string",
      "format": "uri",
      "description": "HTTPS download URL. Publishers SHOULD use durable URLs without embedded credentials or expiring access tokens. Redirects MUST remain HTTPS. A change in the bytes served does not change the artifact's declared identity."
    },
    "cid": {
      "type": "string",
      "format": "cid",
      "description": "Content identifier for the complete artifact file bytes, after decoding HTTP transfer and content encodings but before unpacking any artifact archive. MUST use CIDv1, lowercase base32 encoding, the raw codec (0x55), and a 32-byte SHA-256 (0x12) or BLAKE3 (0x1e) digest, following DASL (https://dasl.ing/cid.html) or BDASL (https://dasl.ing/bdasl.html). Consumers MUST support SHA-256 and MAY support BLAKE3. Publishers SHOULD use SHA-256 for broad interoperability. Consumers MUST NOT treat unsupported hash algorithms as verified. A BLAKE3 CID alone does not supply the proofs or transport needed for verified streaming or range retrieval."
    },
    "size": {
      "type": "integer",
      "minimum": 0,
      "description": "Expected size in bytes of the file identified by the CID, after decoding HTTP content encodings but before unpacking any artifact archive."
    },
    "mimeType": {
      "type": "string",
      "minLength": 1,
      "description": "Optional publisher-declared media type of the artifact file."
    },
    "target": {
      "type": "ref",
      "ref": "#target",
      "description": "Intended runtime target. Omission means compatibility is unspecified, not universal."
    },
    "metadata": {
      "type": "array",
      "items": {
        "type": "union",
        "refs": [],
        "closed": false
      },
      "description": "Typed ecosystem-specific metadata fragments. Metadata supplements core artifact fields and MUST NOT override or contradict them. Consumers may display or offer downloads of artifacts with unknown fragment types, but MUST NOT assume those fragments impose no additional requirements for automated installation. Unknown fragment types do not establish compatibility or suitability for automated installation."
    }
  }
}
fileNamestringRequiredminLength:1maxLength:255
Suggested download filename, not a path. MUST NOT contain slashes, backslashes, or control characters, and MUST NOT be dot or dot-dot. Consumers MUST handle it as untrusted input and ensure it is safe for the destination filesystem. Filenames do not define target compatibility.
urlstringuriRequired
HTTPS download URL. Publishers SHOULD use durable URLs without embedded credentials or expiring access tokens. Redirects MUST remain HTTPS. A change in the bytes served does not change the artifact's declared identity.
cidstringcidRequired
Content identifier for the complete artifact file bytes, after decoding HTTP transfer and content encodings but before unpacking any artifact archive. MUST use CIDv1, lowercase base32 encoding, the raw codec (0x55), and a 32-byte SHA-256 (0x12) or BLAKE3 (0x1e) digest, following DASL (https://dasl.ing/cid.html) or BDASL (https://dasl.ing/bdasl.html). Consumers MUST support SHA-256 and MAY support BLAKE3. Publishers SHOULD use SHA-256 for broad interoperability. Consumers MUST NOT treat unsupported hash algorithms as verified. A BLAKE3 CID alone does not supply the proofs or transport needed for verified streaming or range retrieval.
sizeintegerRequired
Expected size in bytes of the file identified by the CID, after decoding HTTP content encodings but before unpacking any artifact archive.
mimeTypestringOptionalminLength:1
Optional publisher-declared media type of the artifact file.
targetrefOptionalref:#target
Intended runtime target. Omission means compatibility is unspecified, not universal.
metadataarrayOptional
Typed ecosystem-specific metadata fragments. Metadata supplements core artifact fields and MUST NOT override or contradict them. Consumers may display or offer downloads of artifacts with unknown fragment types, but MUST NOT assume those fragments impose no additional requirements for automated installation. Unknown fragment types do not establish compatibility or suitability for automated installation.

Verification

While the software.release.defs#httpArtifact fragment looks very similar to a flattened software.release.defs#blobArtifact fragment, there is one subtle difference that you need to pay attention to: the cid field.

Blob CIDs in ATProto use SHA-256, as specified by DASL CIDs. Consumers can hash a download incrementally, but verification against a whole-file SHA-256 digest requires receiving the complete file.

HTTP artifacts support both DASL CIDs and BDASL CIDs, which add support for BLAKE3. With accompanying hash-tree proofs and a suitable delivery mechanism, BLAKE3 enables consumers to verify portions of a file without downloading it in full. This can be useful for large artifacts, resumable downloads, and range requests.

A BLAKE3 CID and an ordinary HTTPS URL alone do not provide these proofs. In the absence of support for BLAKE3 within atproto itself, we will consider adding lexicon fragments to assist with resolving BLAKE3 sidecar files via blobs or HTTP. These have not been drafted or designed yet.

Consumers must support SHA-256; BLAKE3 support is optional. Publishers should therefore use SHA-256 for broad interoperability, and consider BLAKE3 when their intended consumers explicitly support it—for example, when publishing updates for an application whose updater they control. Browsers do not currently provide native BLAKE3 support, so browser-based consumers need an additional implementation.