diff --git a/docs/operations.md b/docs/operations.md index ade9518..b2234a7 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -84,10 +84,13 @@ PLC key lifecycle: `rotationKeys`. - Existing early accounts can be repaired with `just plc-repair ` after setting `ZDS_DB` or `ZDS_DB_PATH`, `ZDS_PUBLIC_URL`, `ZDS_PLC_ROTATION_KEY`, - and optional `ZDS_RECOVERY_DID_KEY`. The repair tool is intentionally separate - from normal XRPC handling; it only uses the account signing key if the current - public PLC state already authorizes that key as a rotation key, and submits a - replacement operation containing the intended recovery/PDS rotation keys. + and optional `ZDS_REPAIR_RECOVERY_DID_KEY`, `ZDS_REPAIR_RECOVERY_KEY`, or + `ZDS_RECOVERY_DID_KEY`. The repair tool is intentionally separate from normal + XRPC handling; it only uses the account signing key if the current public PLC + state already authorizes that key as a rotation key, and submits a replacement + operation containing the intended recovery/PDS rotation keys. The + `ZDS_REPAIR_RECOVERY_KEY` form accepts a PDS Moover private multikey and + derives the public `did:key` locally. When invites are required and the database has no accounts or invite codes, ZDS creates one bootstrap code and logs it during startup. Invite codes are stored diff --git a/tools/plc_repair.zig b/tools/plc_repair.zig index 955de84..547a125 100644 --- a/tools/plc_repair.zig +++ b/tools/plc_repair.zig @@ -64,7 +64,8 @@ fn env(name: [:0]const u8) ?[]const u8 { } fn targetRotationKeys(allocator: std.mem.Allocator, rotation_did_key: []const u8) ![]const []const u8 { - const recovery = zds.core.config.recoveryDidKey(); + const repair_recovery = try repairRecoveryDidKey(allocator); + const recovery = repair_recovery orelse zds.core.config.recoveryDidKey(); const count: usize = if (recovery == null) 1 else 2; const keys = try allocator.alloc([]const u8, count); var idx: usize = 0; @@ -76,6 +77,16 @@ fn targetRotationKeys(allocator: std.mem.Allocator, rotation_did_key: []const u8 return keys; } +fn repairRecoveryDidKey(allocator: std.mem.Allocator) !?[]const u8 { + if (env("ZDS_REPAIR_RECOVERY_DID_KEY")) |did_key| return did_key; + if (env("ZDS_REPAIR_RECOVERY_KEY")) |private_key| { + var keypair = try zds.atproto.plc.parsePrivateKey(allocator, private_key); + const did_key = try keypair.did(allocator); + return did_key; + } + return null; +} + fn jsonArrayContainsString(value: ?std.json.Value, expected: []const u8) bool { const items = switch (value orelse return false) { .array => |array| array.items,