From 9dc55ced8fc09b6f27d3aa915049190518537c34 Mon Sep 17 00:00:00 2001 From: zzstoatzz Date: Sat, 15 Aug 2026 01:01:32 -0500 Subject: [PATCH] fix: announce pre-existing commit-less accounts at startup Accounts created before createAccount sequenced firehose events have no genesis commit and are invisible to relays. On boot, any active account with no commits now gets the same identity/account/commit/sync announcement as a fresh signup; failures are logged per account and do not block startup. Co-Authored-By: Claude Fable 5 --- src/main.zig | 3 +++ src/storage/store.zig | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/main.zig b/src/main.zig index 0447837..1206302 100644 --- a/src/main.zig +++ b/src/main.zig @@ -67,6 +67,9 @@ pub fn main(init: std.process.Init) !void { zds.core.log.info("bootstrap invite code: {s}\n", .{code}); } } + zds.storage.store.sequenceUnannouncedAccounts(allocator) catch |err| { + zds.core.log.err("startup sequence_unannounced_accounts failed err={s}\n", .{@errorName(err)}); + }; zds.atproto.sync.notifyCrawlers(true); try zds.http.server.listen(io, .{ .host = options.host, .port = options.port }); } diff --git a/src/storage/store.zig b/src/storage/store.zig index 1302743..4a8a9a4 100644 --- a/src/storage/store.zig +++ b/src/storage/store.zig @@ -1225,6 +1225,40 @@ pub fn sequenceNewAccount(allocator: std.mem.Allocator, account: auth.Account) ! try sequenceSyncEvent(allocator, account.did); } +/// Startup repair for accounts created before ZDS sequenced creation events: +/// any active account with no commits gets its genesis announcement now, so +/// relays stop reporting RepoNotFound for it. +pub fn sequenceUnannouncedAccounts(allocator: std.mem.Allocator) !void { + const unannounced = blk: { + db_mutex.lockUncancelable(store_io); + defer db_mutex.unlock(store_io); + try requireInitialized(); + var rows = try conn.rows( + \\SELECT did, handle FROM accounts + \\WHERE activated_at IS NOT NULL AND account_status = 'active' + \\ AND did NOT IN (SELECT did FROM commits) + , .{}); + defer rows.deinit(); + var found: std.ArrayList(auth.Account) = .empty; + while (rows.next()) |row| { + try found.append(allocator, .{ + .did = try allocator.dupe(u8, row.text(0)), + .handle = try allocator.dupe(u8, row.text(1)), + .email = "", + .password = "", + }); + } + break :blk try found.toOwnedSlice(allocator); + }; + for (unannounced) |account| { + sequenceNewAccount(allocator, account) catch |err| { + log.err("store announce failed did={s} handle={s} err={s}\n", .{ account.did, account.handle, @errorName(err) }); + continue; + }; + log.info("store announced pre-existing account did={s} handle={s}\n", .{ account.did, account.handle }); + } +} + pub fn sequenceSyncEvent(allocator: std.mem.Allocator, did: []const u8) !void { db_mutex.lockUncancelable(store_io); defer db_mutex.unlock(store_io); -- 2.51.2