Highly ambitious ATProtocol AppView service and sdks

Fix TID validation

TID format validator was rejecting valid TIDs like `3m3zm7eurxk26`.

It seems like syntax format assumed in the file is wrong, so I amended it to use https://atproto.com/specs/tid#tid-syntax. In particular, see the "reference regex" in the spec, which is `/^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/` and doesn't match old code.

This is vibecoded so be cautious (though both Claude and ChatGPT seem happy with the result). I've also asked Claude to cross-check the new tests against `atproto/packages/syntax/src/tid.ts`.

authored by danabra.mov and committed by Tangled ac8aa7be 32726d63

Changed files
+24 -11
crates
slices-lexicon
src
validation
primitive
+24 -11
crates/slices-lexicon/src/validation/primitive/string.rs
··· 577 578 /// Validates TID (Timestamp Identifier) format 579 /// 580 - /// TID format: 13-character base32-encoded timestamp + random bits 581 - /// Uses Crockford base32 alphabet: 0123456789ABCDEFGHJKMNPQRSTVWXYZ (case-insensitive) 582 pub fn is_valid_tid(&self, value: &str) -> bool { 583 use regex::Regex; 584 ··· 586 return false; 587 } 588 589 - // TID uses Crockford base32 (case-insensitive, excludes I, L, O, U) 590 - let tid_regex = Regex::new(r"^[0-9A-HJKMNP-TV-Z]{13}$").unwrap(); 591 - let uppercase_value = value.to_uppercase(); 592 593 - tid_regex.is_match(&uppercase_value) 594 } 595 596 /// Validates Record Key format ··· 1096 1097 let validator = StringValidator; 1098 1099 - // Valid TIDs (13 characters, Crockford base32) 1100 - assert!(validator.validate_data(&json!("3JZFKJT0000ZZ"), &schema, &ctx).is_ok()); 1101 - assert!(validator.validate_data(&json!("3jzfkjt0000zz"), &schema, &ctx).is_ok()); // case insensitive 1102 1103 - // Invalid TIDs 1104 assert!(validator.validate_data(&json!("too-short"), &schema, &ctx).is_err()); 1105 assert!(validator.validate_data(&json!("too-long-string"), &schema, &ctx).is_err()); 1106 assert!(validator.validate_data(&json!("invalid-chars!"), &schema, &ctx).is_err()); 1107 - assert!(validator.validate_data(&json!("invalid-ILOU0"), &schema, &ctx).is_err()); // invalid chars (I, L, O, U) 1108 } 1109 1110 #[test]
··· 577 578 /// Validates TID (Timestamp Identifier) format 579 /// 580 + /// TID format: 13-character base32-sortable encoded timestamp + random bits 581 + /// Uses ATProto base32-sortable alphabet: 234567abcdefghijklmnopqrstuvwxyz (lowercase only) 582 pub fn is_valid_tid(&self, value: &str) -> bool { 583 use regex::Regex; 584 ··· 586 return false; 587 } 588 589 + // TID uses base32-sortable (s32) - lowercase only 590 + // First character must be from limited set (ensures top bit is 0) 591 + // Remaining 12 characters from full base32-sortable alphabet 592 + let tid_regex = Regex::new(r"^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$").unwrap(); 593 594 + tid_regex.is_match(value) 595 } 596 597 /// Validates Record Key format ··· 1097 1098 let validator = StringValidator; 1099 1100 + // Valid TIDs (base32-sortable, 13 chars, lowercase) 1101 + assert!(validator.validate_data(&json!("3m3zm7eurxk26"), &schema, &ctx).is_ok()); 1102 + assert!(validator.validate_data(&json!("2222222222222"), &schema, &ctx).is_ok()); // minimum TID 1103 + assert!(validator.validate_data(&json!("a222222222222"), &schema, &ctx).is_ok()); // leading 'a' (lower bound) 1104 + assert!(validator.validate_data(&json!("j234567abcdef"), &schema, &ctx).is_ok()); // leading 'j' (upper bound) 1105 + 1106 1107 + // Invalid TIDs - uppercase not allowed (charset is lowercase only) 1108 + assert!(validator.validate_data(&json!("3m3zM7eurxk26"), &schema, &ctx).is_err()); // mixed case 1109 + 1110 + // Invalid TIDs - wrong length 1111 assert!(validator.validate_data(&json!("too-short"), &schema, &ctx).is_err()); 1112 assert!(validator.validate_data(&json!("too-long-string"), &schema, &ctx).is_err()); 1113 + 1114 + // Invalid TIDs - invalid characters (hyphen/punct rejected; digits 0,1,8,9 not allowed) 1115 assert!(validator.validate_data(&json!("invalid-chars!"), &schema, &ctx).is_err()); 1116 + assert!(validator.validate_data(&json!("xyz1234567890"), &schema, &ctx).is_err()); // has 0,1,8,9 1117 + 1118 + // Invalid TIDs - first character must be one of 234567abcdefghij 1119 + assert!(validator.validate_data(&json!("k222222222222"), &schema, &ctx).is_err()); // leading 'k' forbidden 1120 + assert!(validator.validate_data(&json!("z234567abcdef"), &schema, &ctx).is_err()); // leading 'z' forbidden 1121 } 1122 1123 #[test]