Back

Blog details

Anatomy of a CID: Decoding an IPFS Identifier

AIOZ Network
5 min readAugust 22, 2026
aioz-pin

A CID looks like noise until you know what you are looking at: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi or QmYwAPJzv5CZsnAztbSyJmvxG7hcTMBBg9uCPQpNyH1KAF. Both are valid IPFS CIDs, and both encode real, decodable information: a version, a format, and a hash, not just an opaque fingerprint. This breaks a CID down field by field so a string like that stops looking like noise. It is a companion piece to an earlier article on this blog about verifying a CID yourself; this one is about what the identifier is actually made of, not how to recompute it.

TL;DR:

  • A CID has two live versions in use: CIDv0 (the old default, always base58, always starts with "Qm") and CIDv1 (multibase-prefixed, codec-aware, the current default for new content)
  • CIDv1 packs four pieces into one string: a multibase prefix (how the rest is text-encoded), a version number, a multicodec (what format the linked content is in), and a multihash (which hash algorithm plus the digest itself)
  • The hash inside a CID is the hash of the root block of a file's Merkle DAG, not a direct hash of the file's raw bytes
  • CIDv0 strings always decode to sha2-256 hashes of dag-pb-formatted data, since CIDv0 predates the ability to specify either field
  • AIOZ Pin's API returns CIDs from every pinning call; decoding one tells you exactly what hash algorithm and format you are looking at without needing to trust the string blindly

Two Versions, One Underlying Idea

Every CID identifies a piece of content by hashing it, but the two versions in active use encode that differently. CIDv0 is the older, simpler format: always base58-encoded, always 46 characters, always starting with "Qm." It has no way to specify a hash algorithm or content format inside the string itself, both are implicitly fixed (sha2-256, dag-pb) because CIDv0 predates the idea of making them configurable. CIDv1 replaced that with a fully self-describing structure, at the cost of being longer and less immediately recognizable at a glance. New content on IPFS defaults to CIDv1 today; CIDv0 strings still show up constantly because plenty of already-pinned content was hashed before the switch, and both remain valid, permanently, since a CID's whole purpose is to be a stable reference.

The Four Parts of a CIDv1

A CIDv1 string, once decoded, breaks into four distinct fields:

  1. Multibase prefix: the first character of the string, indicating how everything after it is text-encoded. A CID starting with b is base32 (the default for CIDv1), one starting with z is base58btc. This exists because a CID needs to survive being pasted into a URL, a filename, or a QR code, and different contexts tolerate different character sets.
  2. Version: a single number, 1 for CIDv1, encoded right after the multibase prefix is stripped off.
  3. Multicodec: an identifier saying what format the content the CID points to is in. Common values: dag-pb (0x70, the format used for UnixFS files and directories), raw (0x55, an unstructured block with no linking metadata), dag-cbor (0x71, used for structured linked data like the ERC-8004 registration files covered elsewhere on this blog). This is what lets software know how to interpret whatever it fetches, rather than guessing.
  4. Multihash: the actual hash, itself two parts: an algorithm identifier (sha2-256 is the common default, though the format supports others) and the digest, the fixed-length output of running that algorithm.

CIDv0 skips all of this self-description: it is always base58, always version implicitly 0, always dag-pb, always sha2-256, so a CIDv0 string is really just the multihash on its own, with everything else fixed by convention rather than encoded.

What the Hash Actually Hashes

A CID's multihash is not a hash of a file's raw bytes end to end. It is the hash of the file's root block, the top node of its Merkle DAG. For a small file that fits in one block, those two things happen to be close to the same thing. For a large file split into many blocks, the root block itself is mostly a list of links to child blocks, each identified by its own CID, so the root's hash depends on the hashes of everything beneath it without directly containing the file's bytes at all. This is the same property covered in this series' first article: change one byte anywhere in the file, and the change propagates up through every parent hash to the root, giving you a different CID.

Decoding a Real CID

Take bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi apart:

  • b, multibase: base32
  • What follows decodes to: version 1, codec dag-pb (0x70), multihash algorithm sha2-256, then the 32-byte digest itself

Every part of that is recoverable from the string alone, with a CID-decoding library (available in the same @aioznetwork/aioz-pin-sdk dependency tree via the underlying multiformats/cid packages, or standalone via js-multiformats) rather than by trusting a written explanation of what a given prefix "usually" means.

Where This Shows Up on AIOZ Pin

Every pinning call on AIOZ Pin, pinFilesToIPFS(), pinFolderToIPFS(), pinByHash(), returns a CID identifying what got pinned. Knowing how to decode it tells you, directly from the string, what hash algorithm secures that content and what format it is stored in, without needing to take AIOZ Pin's word for it or dig through a dashboard. This matters most for anything you plan to verify independently later, an NFT's metadata file, an ERC-8004 registration file, a dataset you need to prove has not changed, since the CID itself is the durable, checkable record of exactly what you pinned.

Frequently Asked Questions

What is a CID in IPFS? A Content Identifier: a self-describing string that identifies content by the hash of its root Merkle DAG block, along with metadata about which hash algorithm and content format were used.

What is the difference between CIDv0 and CIDv1? CIDv0 is always base58-encoded, 46 characters, and implicitly sha2-256/dag-pb with no way to specify otherwise. CIDv1 is multibase-prefixed and explicitly encodes its version, content codec, and hash algorithm, and is the current default for new content.

Does a CID hash the whole file directly? No. It hashes the file's root Merkle DAG block, which for a multi-block file is mostly links to child blocks, each identified by their own hash, rather than the file's raw bytes directly.

Why do some CIDs start with "Qm" and others with "b"? "Qm" indicates CIDv0 (base58-encoded). "b" indicates CIDv1 encoded in base32, the current default text encoding for new CIDs.

Can two different files ever produce the same CID? Only in the case of a hash collision, which is why IPFS relies on cryptographic hash functions like sha2-256, where finding two inputs with the same output is computationally infeasible.

Does AIOZ Pin use a custom CID format? No. AIOZ Pin returns standard IPFS CIDs from its pinning API, decodable with any standard CID library, not a proprietary identifier format.

References

We only send updates when meaningful changes ship, and you can unsubscribe anytime

Related Content

blog thumbnail

How AIOZ Pin's Wallet Billing Actually Works

AIOZ Pin has no credit card option. You fund an account with AIOZ tokens instead. Here is exactly how the wallet, deposits, and conversion actually work.

5 min readSeptember 13, 2026
blog thumbnail

Content Moderation on IPFS: What Actually Happens

Pinning services can remove their own copy of a file. They cannot remove it from IPFS. Here is exactly what a takedown does and does not accomplish.

5 min readSeptember 12, 2026
blog thumbnail

IPFS Transports: TCP vs. QUIC Explained

libp2p connections can run over more than one transport. Here is exactly what changes with QUIC instead of TCP, and why IPFS dials both at once.

5 min readSeptember 11, 2026
blog thumbnail

GossipSub: How IPFS's PubSub Layer Actually Works

The DHT answers who has a CID. GossipSub answers a different question: how do peers push real-time updates to everyone listening, without a lookup at all.

5 min readSeptember 10, 2026
blog thumbnail

Helia: IPFS's Modern JavaScript Implementation

js-ipfs is deprecated. Helia is what replaced it: a modular, TypeScript-first IPFS implementation built for the browser and Node.js. Here is how it fits.

5 min readSeptember 09, 2026
blog thumbnail

IPLD Selectors: Fetching Part of a DAG, Not All of It

Not every retrieval needs the whole DAG. IPLD Selectors describe exactly which nodes to traverse and match, so a client can fetch a slice, not everything.

5 min readSeptember 08, 2026