A library for ATProtocol identities.

feature: Added stdin support to binaries.

* The atproto-record-sign tool can read records from stdin.
* The atproto-record-verify tool can read records from stdin.
* Added a top-level project README.md file.
* Added missing LICENSE file.

Changed files
+325 -19
crates
+2
CLAUDE.prompts.md
··· 24 25 Write a project `README.md` file that describes the project as a library that supports ATProtocol identity record signing and verifying. Note that parts of this was extracted from the open sourced https://tangled.sh/@smokesignal.events/smokesignal project. This project is open source under the MIT license. 26 27 ## Check and clippy 28 29 Using `cargo clippy`, satisfy warnings. Think very hard about how to do this.
··· 24 25 Write a project `README.md` file that describes the project as a library that supports ATProtocol identity record signing and verifying. Note that parts of this was extracted from the open sourced https://tangled.sh/@smokesignal.events/smokesignal project. This project is open source under the MIT license. 26 27 + The `REAADME.md` file should provide a high level overview of both the `atproto-identity` and `atproto-record` crates. It should also concisely reference the available binaries and provide a minimal example of how to use them. 28 + 29 ## Check and clippy 30 31 Using `cargo clippy`, satisfy warnings. Think very hard about how to do this.
+9
LICENSE
···
··· 1 + MIT License 2 + 3 + Copyright (c) 2024 Nick Gerakines 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 + 7 + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 + 9 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+220
README.md
···
··· 1 + # AT Protocol Identity & Record Library 2 + 3 + A comprehensive Rust library for AT Protocol identity management and record signing/verification. This library provides full functionality for DID resolution, handle resolution, identity document management, and cryptographic record operations across multiple DID methods. 4 + 5 + Parts of this library were extracted from the open-sourced [Smokesignal](https://tangled.sh/@smokesignal.events/smokesignal) project. 6 + 7 + ## Crates 8 + 9 + This workspace contains two main crates: 10 + 11 + ### `atproto-identity` 12 + 13 + A comprehensive AT Protocol identity management library providing: 14 + 15 + - **DID Resolution**: Support for `did:plc`, `did:web`, and `did:key` methods 16 + - **Handle Resolution**: DNS-based handle resolution with validation 17 + - **Identity Documents**: Complete DID document parsing and management 18 + - **Cryptographic Operations**: P-256 and K-256 elliptic curve support 19 + - **Validation**: Input validation for handles and DIDs 20 + - **Configuration**: Environment-based configuration management 21 + 22 + ### `atproto-record` 23 + 24 + AT Protocol record signature operations library providing: 25 + 26 + - **Record Signing**: Create cryptographic signatures for AT Protocol records 27 + - **Signature Verification**: Verify existing signatures against records and public keys 28 + - **IPLD Integration**: Proper IPLD DAG-CBOR serialization for signature content 29 + - **Multi-curve Support**: Support for P-256 and K-256 elliptic curves 30 + 31 + ## CLI Tools 32 + 33 + The library includes several command-line utilities: 34 + 35 + ### atproto-identity 36 + 37 + - `atproto-identity-resolve` - Resolve DIDs and handles to identity documents 38 + - `atproto-identity-sign` - Sign identity-related operations 39 + - `atproto-identity-validate` - Validate DID and handle formats 40 + 41 + ### atproto-record 42 + 43 + - `atproto-record-sign` - Sign AT Protocol records from files or stdin 44 + - `atproto-record-verify` - Verify AT Protocol record signatures from files or stdin 45 + 46 + ## Quick Start 47 + 48 + Add the crates to your `Cargo.toml`: 49 + 50 + ```toml 51 + [dependencies] 52 + atproto-identity = "0.3.0" 53 + atproto-record = "0.3.0" 54 + ``` 55 + 56 + ### Basic Identity Resolution 57 + 58 + ```rust 59 + use atproto_identity::resolve::resolve_handle; 60 + 61 + #[tokio::main] 62 + async fn main() -> anyhow::Result<()> { 63 + // Resolve a handle to a DID 64 + let did = resolve_handle("alice.bsky.social").await?; 65 + println!("Resolved DID: {}", did); 66 + 67 + Ok(()) 68 + } 69 + ``` 70 + 71 + ### Record Signing and Verification 72 + 73 + ```rust 74 + use atproto_identity::key::identify_key; 75 + use atproto_record::signature; 76 + use serde_json::json; 77 + 78 + #[tokio::main] 79 + async fn main() -> anyhow::Result<()> { 80 + // Parse DID key for signing operations 81 + let signing_key = identify_key("did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW")?; 82 + 83 + // Create a record to sign 84 + let record = json!({ 85 + "$type": "app.bsky.feed.post", 86 + "text": "Hello AT Protocol!", 87 + "createdAt": "2024-01-01T00:00:00.000Z" 88 + }); 89 + 90 + // Create signature object with issuer and timestamp 91 + let signature_object = json!({ 92 + "issuer": "did:plc:tgudj2fjm77pzkuawquqhsxm", 93 + "issued_at": "2024-01-01T00:00:00.000Z" 94 + }); 95 + 96 + // Sign the record 97 + let signed_record = signature::create( 98 + &signing_key, 99 + &record, 100 + "did:plc:4zutorghlchjxzgceklue4la", // repository 101 + "app.bsky.feed.post", // collection 102 + signature_object, 103 + ).await?; 104 + 105 + // Verify the signature 106 + signature::verify( 107 + "did:plc:tgudj2fjm77pzkuawquqhsxm", // issuer 108 + &signing_key, // verification key 109 + signed_record, // signed record 110 + "did:plc:4zutorghlchjxzgceklue4la", // repository 111 + "app.bsky.feed.post", // collection 112 + ).await?; 113 + 114 + println!("Signature verification successful"); 115 + 116 + Ok(()) 117 + } 118 + ``` 119 + 120 + ### CLI Usage Examples 121 + 122 + ```bash 123 + # Resolve a handle or DID 124 + cargo run --bin atproto-identity-resolve -- alice.bsky.social 125 + 126 + # Get full DID document 127 + cargo run --bin atproto-identity-resolve -- --did-document did:plc:abc123 128 + 129 + # Sign a record from file 130 + cargo run --bin atproto-record-sign -- \ 131 + did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \ 132 + ./record.json \ 133 + did:plc:tgudj2fjm77pzkuawquqhsxm \ 134 + repository=did:plc:4zutorghlchjxzgceklue4la \ 135 + collection=app.bsky.feed.post 136 + 137 + # Sign a record from stdin 138 + echo '{"$type":"app.bsky.feed.post","text":"Hello!"}' | \ 139 + cargo run --bin atproto-record-sign -- \ 140 + did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \ 141 + -- \ 142 + did:plc:tgudj2fjm77pzkuawquqhsxm \ 143 + repository=did:plc:4zutorghlchjxzgceklue4la \ 144 + collection=app.bsky.feed.post 145 + 146 + # Verify a signature from file 147 + cargo run --bin atproto-record-verify -- \ 148 + did:plc:tgudj2fjm77pzkuawquqhsxm \ 149 + did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \ 150 + ./signed_record.json \ 151 + repository=did:plc:4zutorghlchjxzgceklue4la \ 152 + collection=app.bsky.feed.post 153 + 154 + # Verify a signature from stdin 155 + echo '{"signatures":[...],"text":"Hello!"}' | \ 156 + cargo run --bin atproto-record-verify -- \ 157 + did:plc:tgudj2fjm77pzkuawquqhsxm \ 158 + did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \ 159 + -- \ 160 + repository=did:plc:4zutorghlchjxzgceklue4la \ 161 + collection=app.bsky.feed.post 162 + ``` 163 + 164 + ## Development 165 + 166 + ### Building 167 + 168 + ```bash 169 + cargo build 170 + ``` 171 + 172 + ### Running Tests 173 + 174 + ```bash 175 + cargo test 176 + ``` 177 + 178 + ### Code Quality 179 + 180 + ```bash 181 + # Format code 182 + cargo fmt 183 + 184 + # Lint 185 + cargo clippy 186 + 187 + # Check without building 188 + cargo check 189 + ``` 190 + 191 + ## Features 192 + 193 + - **Async/Await**: Built with modern Rust async patterns using Tokio 194 + - **Error Handling**: Comprehensive structured error types using `thiserror` 195 + - **Logging**: Structured logging with `tracing` 196 + - **Security**: Forbids unsafe code and follows security best practices 197 + - **Standards Compliance**: Full AT Protocol specification compliance 198 + - **Multi-platform**: Works on all major platforms 199 + 200 + ## Architecture 201 + 202 + The library follows a modular architecture with clear separation of concerns: 203 + 204 + - **Identity Management**: Handle DID resolution, validation, and document management 205 + - **Cryptographic Operations**: Secure key operations and signature handling 206 + - **Network Operations**: HTTP and DNS resolution with proper error handling 207 + - **Data Models**: Comprehensive type definitions for AT Protocol entities 208 + - **CLI Tools**: Ready-to-use command-line utilities 209 + 210 + ## License 211 + 212 + MIT License - see [LICENSE](LICENSE) for details. 213 + 214 + ## Contributing 215 + 216 + Contributions are welcome! This project follows standard Rust conventions and includes comprehensive testing and documentation requirements. 217 + 218 + ## Repository 219 + 220 + https://tangled.sh/@smokesignal.events/atproto-identity-rs
+47 -9
crates/atproto-record/src/bin/atproto-record-sign.rs
··· 6 use atproto_record::signature::create; 7 use chrono::{SecondsFormat, Utc}; 8 use serde_json::json; 9 - use std::{collections::HashMap, env, fs}; 10 11 /// AT Protocol Record Signing Tool 12 /// 13 /// This command-line tool provides cryptographic signing capabilities for AT Protocol records. 14 - /// It reads a JSON record from a file, applies a cryptographic signature using a DID key, 15 /// and outputs the signed record with embedded signature metadata. 16 /// 17 /// ## Overview ··· 19 /// The tool performs the following operations: 20 /// 1. **Command Line Parsing**: Extracts signing parameters from command line arguments 21 /// 2. **Key Resolution**: Converts DID key strings to cryptographic key material 22 - /// 3. **Record Loading**: Reads and parses JSON records from disk files 23 /// 4. **Signature Creation**: Generates cryptographic signatures using IPLD DAG-CBOR serialization 24 /// 5. **Output Generation**: Produces signed records with embedded signature objects 25 /// ··· 37 /// The tool accepts flexible argument ordering: 38 /// - **DID Key** (`did:key:...`) - Cryptographic key for signing operations 39 /// - **Issuer DID** (`did:plc:...` or `did:web:...`) - Identity of the signature issuer 40 - /// - **Record File** (file path) - JSON file containing the record to sign 41 /// - **Parameters** (`key=value`) - Repository, collection, and signature metadata 42 /// 43 /// ## Required Parameters ··· 73 /// issued_at=2025-05-16T14:00:02.000Z 74 /// ``` 75 /// 76 /// ### Input Record Example (`post.json`) 77 /// ```json 78 /// { ··· 121 println!("AT Protocol Record Signing Tool"); 122 println!(); 123 println!("USAGE:"); 124 - println!(" atproto-record-sign <ISSUER_DID> <SIGNING_KEY> <RECORD_FILE> repository=<REPO> collection=<COLLECTION> [key=value...]"); 125 println!(); 126 println!("ARGUMENTS:"); 127 - println!(" <ISSUER_DID> DID of the issuer (e.g., did:plc:...)"); 128 - println!(" <SIGNING_KEY> DID key for signing (e.g., did:key:z42tv1...)"); 129 - println!(" <RECORD_FILE> Path to JSON file containing the record to sign"); 130 println!(); 131 println!("REQUIRED PARAMETERS:"); 132 println!(" repository=<REPO> Repository DID context"); ··· 136 println!(" issued_at=<TIMESTAMP> RFC 3339 timestamp (defaults to current time)"); 137 println!(" [key=value...] Additional fields for signature object"); 138 println!(); 139 - println!("EXAMPLE:"); 140 println!(" atproto-record-sign \\"); 141 println!(" did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \\"); 142 println!(" ./record.json \\"); 143 println!(" did:plc:tgudj2fjm77pzkuawquqhsxm \\"); 144 println!(" repository=did:plc:4zutorghlchjxzgceklue4la \\"); 145 println!(" collection=community.lexicon.badge.award"); 146 return Ok(()); 147 } 148 ··· 178 } 179 Ok(_) => return Err(anyhow!("Unsupported DID method: {}", argument)), 180 Err(e) => return Err(anyhow!("Failed to parse DID {}: {}", argument, e)), 181 } 182 } else { 183 // Assume it's a file path to read the record from
··· 6 use atproto_record::signature::create; 7 use chrono::{SecondsFormat, Utc}; 8 use serde_json::json; 9 + use std::{ 10 + collections::HashMap, 11 + env, fs, 12 + io::{self, Read}, 13 + }; 14 15 /// AT Protocol Record Signing Tool 16 /// 17 /// This command-line tool provides cryptographic signing capabilities for AT Protocol records. 18 + /// It reads a JSON record from a file or stdin, applies a cryptographic signature using a DID key, 19 /// and outputs the signed record with embedded signature metadata. 20 /// 21 /// ## Overview ··· 23 /// The tool performs the following operations: 24 /// 1. **Command Line Parsing**: Extracts signing parameters from command line arguments 25 /// 2. **Key Resolution**: Converts DID key strings to cryptographic key material 26 + /// 3. **Record Loading**: Reads and parses JSON records from disk files or stdin 27 /// 4. **Signature Creation**: Generates cryptographic signatures using IPLD DAG-CBOR serialization 28 /// 5. **Output Generation**: Produces signed records with embedded signature objects 29 /// ··· 41 /// The tool accepts flexible argument ordering: 42 /// - **DID Key** (`did:key:...`) - Cryptographic key for signing operations 43 /// - **Issuer DID** (`did:plc:...` or `did:web:...`) - Identity of the signature issuer 44 + /// - **Record Input** (file path or `--`) - JSON file containing the record to sign, or `--` to read from stdin 45 /// - **Parameters** (`key=value`) - Repository, collection, and signature metadata 46 /// 47 /// ## Required Parameters ··· 77 /// issued_at=2025-05-16T14:00:02.000Z 78 /// ``` 79 /// 80 + /// ### Reading from Stdin 81 + /// ```bash 82 + /// echo '{"$type":"app.bsky.feed.post","text":"Hello from stdin!"}' | \ 83 + /// atproto-record-sign \ 84 + /// did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \ 85 + /// -- \ 86 + /// did:plc:tgudj2fjm77pzkuawquqhsxm \ 87 + /// repository=did:plc:4zutorghlchjxzgceklue4la \ 88 + /// collection=app.bsky.feed.post 89 + /// ``` 90 + /// 91 /// ### Input Record Example (`post.json`) 92 /// ```json 93 /// { ··· 136 println!("AT Protocol Record Signing Tool"); 137 println!(); 138 println!("USAGE:"); 139 + println!(" atproto-record-sign <ISSUER_DID> <SIGNING_KEY> <RECORD_INPUT> repository=<REPO> collection=<COLLECTION> [key=value...]"); 140 println!(); 141 println!("ARGUMENTS:"); 142 + println!(" <ISSUER_DID> DID of the issuer (e.g., did:plc:...)"); 143 + println!(" <SIGNING_KEY> DID key for signing (e.g., did:key:z42tv1...)"); 144 + println!(" <RECORD_INPUT> Path to JSON file containing the record to sign, or '--' to read from stdin"); 145 println!(); 146 println!("REQUIRED PARAMETERS:"); 147 println!(" repository=<REPO> Repository DID context"); ··· 151 println!(" issued_at=<TIMESTAMP> RFC 3339 timestamp (defaults to current time)"); 152 println!(" [key=value...] Additional fields for signature object"); 153 println!(); 154 + println!("EXAMPLES:"); 155 + println!(" # Sign from file:"); 156 println!(" atproto-record-sign \\"); 157 println!(" did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \\"); 158 println!(" ./record.json \\"); 159 println!(" did:plc:tgudj2fjm77pzkuawquqhsxm \\"); 160 println!(" repository=did:plc:4zutorghlchjxzgceklue4la \\"); 161 println!(" collection=community.lexicon.badge.award"); 162 + println!(); 163 + println!(" # Sign from stdin:"); 164 + println!(" echo '{{\"$type\":\"app.bsky.feed.post\",\"text\":\"Hello!\"}}' | atproto-record-sign \\"); 165 + println!(" did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \\"); 166 + println!(" -- \\"); 167 + println!(" did:plc:tgudj2fjm77pzkuawquqhsxm \\"); 168 + println!(" repository=did:plc:4zutorghlchjxzgceklue4la \\"); 169 + println!(" collection=app.bsky.feed.post"); 170 return Ok(()); 171 } 172 ··· 202 } 203 Ok(_) => return Err(anyhow!("Unsupported DID method: {}", argument)), 204 Err(e) => return Err(anyhow!("Failed to parse DID {}: {}", argument, e)), 205 + } 206 + } else if argument == "--" { 207 + // Read record from stdin 208 + if record.is_none() { 209 + let mut stdin_content = String::new(); 210 + io::stdin() 211 + .read_to_string(&mut stdin_content) 212 + .map_err(|e| anyhow!("Failed to read from stdin: {}", e))?; 213 + record = Some( 214 + serde_json::from_str(&stdin_content) 215 + .map_err(|e| anyhow!("Failed to parse JSON from stdin: {}", e))?, 216 + ); 217 + } else { 218 + return Err(anyhow!("Unexpected argument: {}", argument)); 219 } 220 } else { 221 // Assume it's a file path to read the record from
+47 -10
crates/atproto-record/src/bin/atproto-record-verify.rs
··· 4 resolve::{parse_input, InputType}, 5 }; 6 use atproto_record::signature::verify; 7 - use std::{env, fs}; 8 9 /// AT Protocol Record Verification Tool 10 /// 11 /// This command-line tool provides cryptographic signature verification capabilities for AT Protocol records. 12 - /// It reads a signed JSON record from a file, validates the embedded cryptographic signatures using a public key, 13 /// and reports whether the signature verification succeeds or fails. 14 /// 15 /// ## Overview ··· 17 /// The tool performs the following operations: 18 /// 1. **Command Line Parsing**: Extracts verification parameters from command line arguments 19 /// 2. **Key Resolution**: Converts DID key strings to cryptographic key material for verification 20 - /// 3. **Record Loading**: Reads and parses signed JSON records from disk files 21 /// 4. **Signature Verification**: Validates cryptographic signatures using IPLD DAG-CBOR deserialization 22 /// 5. **Result Reporting**: Outputs verification success or detailed failure information 23 /// ··· 37 /// The tool accepts flexible argument ordering: 38 /// - **Issuer DID** (`did:plc:...` or `did:web:...`) - Identity of the expected signature issuer 39 /// - **Verification Key** (`did:key:...`) - Public key for signature verification 40 - /// - **Record File** (file path) - JSON file containing the signed record to verify 41 /// - **Parameters** (`key=value`) - Repository and collection context for verification 42 /// 43 /// ## Required Parameters ··· 67 /// collection=community.lexicon.badge.award 68 /// ``` 69 /// 70 /// ### Input Signed Record Example (`signed_post.json`) 71 /// ```json 72 /// { ··· 138 println!("AT Protocol Record Verifying Tool"); 139 println!(); 140 println!("USAGE:"); 141 - println!(" atproto-record-verify <ISSUER_DID> <KEY> <RECORD_FILE> repository=<REPO> collection=<COLLECTION> [key=value...]"); 142 println!(); 143 println!("ARGUMENTS:"); 144 - println!(" <ISSUER_DID> DID of the issuer (e.g., did:plc:...)"); 145 - println!(" <KEY> DID key for verifying (e.g., did:key:z42tv1...)"); 146 - println!(" <RECORD_FILE> Path to JSON file containing the record to verify"); 147 println!(); 148 println!("REQUIRED PARAMETERS:"); 149 println!(" repository=<REPO> Repository DID context"); 150 println!(" collection=<COLLECTION> Collection name context"); 151 println!(); 152 - println!("EXAMPLE:"); 153 println!(" atproto-record-verify \\"); 154 println!(" did:plc:tgudj2fjm77pzkuawquqhsxm \\"); 155 println!(" did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \\"); 156 - println!(" ./record.json \\"); 157 println!(" repository=did:plc:4zutorghlchjxzgceklue4la \\"); 158 println!(" collection=community.lexicon.badge.award"); 159 return Ok(()); 160 } 161 ··· 188 } 189 Ok(_) => return Err(anyhow!("Unsupported DID method: {}", argument)), 190 Err(e) => return Err(anyhow!("Failed to parse DID {}: {}", argument, e)), 191 } 192 } else { 193 // Assume it's a file path to read the record from
··· 4 resolve::{parse_input, InputType}, 5 }; 6 use atproto_record::signature::verify; 7 + use std::{ 8 + env, fs, 9 + io::{self, Read}, 10 + }; 11 12 /// AT Protocol Record Verification Tool 13 /// 14 /// This command-line tool provides cryptographic signature verification capabilities for AT Protocol records. 15 + /// It reads a signed JSON record from a file or stdin, validates the embedded cryptographic signatures using a public key, 16 /// and reports whether the signature verification succeeds or fails. 17 /// 18 /// ## Overview ··· 20 /// The tool performs the following operations: 21 /// 1. **Command Line Parsing**: Extracts verification parameters from command line arguments 22 /// 2. **Key Resolution**: Converts DID key strings to cryptographic key material for verification 23 + /// 3. **Record Loading**: Reads and parses signed JSON records from disk files or stdin 24 /// 4. **Signature Verification**: Validates cryptographic signatures using IPLD DAG-CBOR deserialization 25 /// 5. **Result Reporting**: Outputs verification success or detailed failure information 26 /// ··· 40 /// The tool accepts flexible argument ordering: 41 /// - **Issuer DID** (`did:plc:...` or `did:web:...`) - Identity of the expected signature issuer 42 /// - **Verification Key** (`did:key:...`) - Public key for signature verification 43 + /// - **Record Input** (file path or `--`) - JSON file containing the signed record to verify, or `--` to read from stdin 44 /// - **Parameters** (`key=value`) - Repository and collection context for verification 45 /// 46 /// ## Required Parameters ··· 70 /// collection=community.lexicon.badge.award 71 /// ``` 72 /// 73 + /// ### Verifying from Stdin 74 + /// ```bash 75 + /// echo '{"signatures":[...],"text":"Hello!"}' | \ 76 + /// atproto-record-verify \ 77 + /// did:plc:tgudj2fjm77pzkuawquqhsxm \ 78 + /// did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \ 79 + /// -- \ 80 + /// repository=did:plc:4zutorghlchjxzgceklue4la \ 81 + /// collection=app.bsky.feed.post 82 + /// ``` 83 + /// 84 /// ### Input Signed Record Example (`signed_post.json`) 85 /// ```json 86 /// { ··· 152 println!("AT Protocol Record Verifying Tool"); 153 println!(); 154 println!("USAGE:"); 155 + println!(" atproto-record-verify <ISSUER_DID> <KEY> <RECORD_INPUT> repository=<REPO> collection=<COLLECTION> [key=value...]"); 156 println!(); 157 println!("ARGUMENTS:"); 158 + println!(" <ISSUER_DID> DID of the issuer (e.g., did:plc:...)"); 159 + println!(" <KEY> DID key for verifying (e.g., did:key:z42tv1...)"); 160 + println!(" <RECORD_INPUT> Path to JSON file containing the record to verify, or '--' to read from stdin"); 161 println!(); 162 println!("REQUIRED PARAMETERS:"); 163 println!(" repository=<REPO> Repository DID context"); 164 println!(" collection=<COLLECTION> Collection name context"); 165 println!(); 166 + println!("EXAMPLES:"); 167 + println!(" # Verify from file:"); 168 println!(" atproto-record-verify \\"); 169 println!(" did:plc:tgudj2fjm77pzkuawquqhsxm \\"); 170 println!(" did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \\"); 171 + println!(" ./signed_record.json \\"); 172 println!(" repository=did:plc:4zutorghlchjxzgceklue4la \\"); 173 println!(" collection=community.lexicon.badge.award"); 174 + println!(); 175 + println!(" # Verify from stdin:"); 176 + println!(" echo '{{\"signatures\":[...],...}}' | atproto-record-verify \\"); 177 + println!(" did:plc:tgudj2fjm77pzkuawquqhsxm \\"); 178 + println!(" did:key:z42tv1pb3Dzog28Q1udyieg1YJP3x1Un5vraE1bttXeCDSpW \\"); 179 + println!(" -- \\"); 180 + println!(" repository=did:plc:4zutorghlchjxzgceklue4la \\"); 181 + println!(" collection=app.bsky.feed.post"); 182 return Ok(()); 183 } 184 ··· 211 } 212 Ok(_) => return Err(anyhow!("Unsupported DID method: {}", argument)), 213 Err(e) => return Err(anyhow!("Failed to parse DID {}: {}", argument, e)), 214 + } 215 + } else if argument == "--" { 216 + // Read record from stdin 217 + if record.is_none() { 218 + let mut stdin_content = String::new(); 219 + io::stdin() 220 + .read_to_string(&mut stdin_content) 221 + .map_err(|e| anyhow!("Failed to read from stdin: {}", e))?; 222 + record = Some( 223 + serde_json::from_str(&stdin_content) 224 + .map_err(|e| anyhow!("Failed to parse JSON from stdin: {}", e))?, 225 + ); 226 + } else { 227 + return Err(anyhow!("Unexpected argument: {}", argument)); 228 } 229 } else { 230 // Assume it's a file path to read the record from