From 93bb8e7c6e6346db0da5eca87732e7fe9257995d Mon Sep 17 00:00:00 2001 From: zzstoatzz Date: Wed, 12 Aug 2026 13:25:07 -0500 Subject: [PATCH] wire: sync blocks are the atproto $bytes sentinel, not a bare base64 string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the d4dd2f0 differential oracle's live cutover rejected our #sync frames: a lexicon bytes value is {"$bytes":""} per the data-model spec (atmos cbor.AppendJSONBytes documents that a bare string is rejected by conformant decoders — and it was). all seven upstream oracle tests pass locally against the pin. Co-Authored-By: Claude Opus 5 (1M context) --- src/internal/serve/wire.zig | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/internal/serve/wire.zig b/src/internal/serve/wire.zig index 46841fd..599d1f9 100644 --- a/src/internal/serve/wire.zig +++ b/src/internal/serve/wire.zig @@ -513,8 +513,18 @@ pub fn encodeSyncV2(w: *std.Io.Writer, args: struct { try s.write(seq); try s.objectField("sync"); try s.beginObject(); + // lexicon "bytes" value: the {"$bytes":""} sentinel + // (atmos cbor.AppendJSONBytes) — a bare base64 string is rejected by + // conformant decoders (caught live by the d4dd2f0 differential oracle) try s.objectField("blocks"); - try writeBase64String(&s, args.blocks); + try s.beginObject(); + try s.objectField("$bytes"); + try s.beginWriteRaw(); + try s.writer.writeByte('"'); + try writeBase64NoPad(s.writer, args.blocks); + try s.writer.writeByte('"'); + s.endWriteRaw(); + try s.endObject(); try s.objectField("did"); try writeString(&s, args.did); try s.objectField("rev"); @@ -534,22 +544,6 @@ pub fn encodeSyncV2(w: *std.Io.Writer, args: struct { try w.writeByte('}'); } -/// base64 std WITH padding, matching Go's []byte JSON encoding -fn writeBase64String(s: *std.json.Stringify, data: []const u8) !void { - try s.beginWriteRaw(); - try s.writer.writeByte('"'); - const encoder = std.base64.standard.Encoder; - var chunk: [4]u8 = undefined; - var i: usize = 0; - while (i < data.len) : (i += 3) { - const n = @min(3, data.len - i); - const out = encoder.encode(&chunk, data[i..][0..n]); - try s.writer.writeAll(out); - } - try s.writer.writeByte('"'); - s.endWriteRaw(); -} - /// #info advisory (seq-less): {"$type":"message","payload":{"$type":"...#info","message"?,"name"}} pub fn encodeInfoV2(w: *std.Io.Writer, name: []const u8, message: ?[]const u8) !void { try w.writeAll(message_frame_prefix); -- 2.51.2