atproto relay implementation in zig zlay.waow.tech
9
fork

Configure Feed

Select the types of activity you want to include in your feed.

ensure tooBig: false on outgoing commit frames

some PDSes omit the tooBig field from commit events. the lexicon marks it
required, and consumers like hydrant reject frames without it.

resequenceFrame now detects #commit frames and injects tooBig: false when
the field is missing. non-commit frames are unaffected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+28
+28
src/broadcaster.zig
··· 204 204 const header_end = header_result.consumed; 205 205 const payload_data = data[header_end..]; 206 206 207 + // check header type — commit frames need tooBig normalization 208 + const is_commit = blk: { 209 + const header_val = switch (header_result.value) { 210 + .map => |entries| entries, 211 + else => break :blk false, 212 + }; 213 + for (header_val) |entry| { 214 + if (std.mem.eql(u8, entry.key, "t")) { 215 + const t = switch (entry.value) { 216 + .text => |s| s, 217 + else => break :blk false, 218 + }; 219 + break :blk std.mem.eql(u8, t, "#commit"); 220 + } 221 + } 222 + break :blk false; 223 + }; 224 + 207 225 // decode payload map 208 226 const payload = cbor.decodeAll(allocator, payload_data) catch return null; 209 227 const old_entries = switch (payload) { ··· 214 232 // rebuild map entries with seq replaced 215 233 var new_entries: std.ArrayListUnmanaged(cbor.Value.MapEntry) = .empty; 216 234 var found_seq = false; 235 + var found_too_big = false; 217 236 for (old_entries) |entry| { 218 237 if (std.mem.eql(u8, entry.key, "seq")) { 219 238 new_entries.append(allocator, .{ .key = "seq", .value = .{ .unsigned = relay_seq } }) catch return null; 220 239 found_seq = true; 221 240 } else { 241 + if (std.mem.eql(u8, entry.key, "tooBig")) found_too_big = true; 222 242 new_entries.append(allocator, entry) catch return null; 223 243 } 224 244 } 225 245 if (!found_seq) { 226 246 new_entries.append(allocator, .{ .key = "seq", .value = .{ .unsigned = relay_seq } }) catch return null; 247 + } 248 + // lexicon requires tooBig on #commit — some PDSes omit it, hydrant et al. reject the frame 249 + if (is_commit and !found_too_big) { 250 + new_entries.append(allocator, .{ .key = "tooBig", .value = .{ .boolean = false } }) catch return null; 227 251 } 228 252 229 253 const new_payload: cbor.Value = .{ .map = new_entries.items }; ··· 1420 1444 // other fields preserved 1421 1445 try std.testing.expectEqualStrings("did:plc:test123", p.getString("repo").?); 1422 1446 try std.testing.expectEqualStrings("3k2abc000000", p.getString("rev").?); 1447 + // tooBig injected for commit frames missing it 1448 + try std.testing.expectEqual(false, p.getBool("tooBig").?); 1423 1449 } 1424 1450 1425 1451 test "resequenceFrame preserves identity frame fields" { ··· 1453 1479 try std.testing.expectEqual(@as(u64, 42), p.getUint("seq").?); 1454 1480 try std.testing.expectEqualStrings("did:plc:alice", p.getString("did").?); 1455 1481 try std.testing.expectEqualStrings("2024-01-15T10:30:00Z", p.getString("time").?); 1482 + // tooBig NOT injected for non-commit frames 1483 + try std.testing.expectEqual(@as(?bool, null), p.getBool("tooBig")); 1456 1484 } 1457 1485 1458 1486 test "encodeErrorFrame produces valid error frame CBOR" {