use anyhow::Result;
use atproto_record::errors::CliError;
use cid::Cid;
use clap::Parser;
use multihash::Multihash;
use sha2::{Digest, Sha256};
use std::io::{self, Read};
#[derive(Parser)]
#[command(
name = "atproto-record-cid",
version,
about = "Generate CID for AT Protocol DAG-CBOR records from JSON",
long_about = "
A command-line tool for generating Content Identifiers (CIDs) from JSON records
using the AT Protocol DAG-CBOR serialization format.
The tool reads JSON from stdin, serializes it using IPLD DAG-CBOR format, and
outputs the corresponding CID using CIDv1 with SHA-256 hashing. This matches
the AT Protocol specification for content addressing of records.
CID FORMAT:
Version: CIDv1
Codec: DAG-CBOR (0x71)
Hash: SHA-256 (0x12)
Encoding: Base32 (default for CIDv1)
EXAMPLES:
# Generate CID from stdin:
echo '{\"text\":\"Hello!\"}' | atproto-record-cid
# Generate CID from a file:
cat post.json | atproto-record-cid
# Complex record with AT Protocol fields:
echo '{
\"$type\": \"app.bsky.feed.post\",
\"text\": \"Hello world\",
\"createdAt\": \"2025-01-19T10:00:00.000Z\"
}' | atproto-record-cid
OUTPUT:
The tool outputs a single line containing the CID:
bafyreibjzlvhtyxnhbvvzl3gj4qmg2ufl2jbhh5qr3gvvxlm7ksf3qwxqq
NOTES:
- Input must be valid JSON
- The same JSON input will always produce the same CID
- Field order in JSON objects may affect the CID due to DAG-CBOR serialization
- Special AT Protocol fields like $type, $sig, and $link are preserved
"
)]
struct Args {}
fn main() -> Result<()> {
let _args = Args::parse();
let mut stdin_content = String::new();
io::stdin()
.read_to_string(&mut stdin_content)
.map_err(|_| CliError::StdinReadFailed)?;
let json_value: serde_json::Value =
serde_json::from_str(&stdin_content).map_err(|_| CliError::StdinJsonParseFailed)?;
let dag_cbor_bytes =
atproto_dasl::to_vec(&json_value).map_err(|error| CliError::RecordSerializationFailed {
error: error.to_string(),
})?;
let mut hasher = Sha256::new();
hasher.update(&dag_cbor_bytes);
let hash_result = hasher.finalize();
let multihash =
Multihash::wrap(0x12, &hash_result).map_err(|error| CliError::CidGenerationFailed {
error: error.to_string(),
})?;
let cid = Cid::new_v1(0x71, multihash);
println!("{}", cid);
Ok(())
}