From 33f4c648e9ae9f44fda27cf12aa5c2cb8b4850de Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Jul 2026 22:57:25 +0000 Subject: [PATCH] fix(graperank): don't create the operator master for a read-only follower crawl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `amy graperank followers ` (no --relay) assembles the relay universe via allKnownRelays, which read the reachability cache through ctx.reachability — and that lazily derives the monitor key from the operator master, forcing a passphrase prompt (or failing on a fresh machine) purely to READ kind:30166 records. The follower crawl signs nothing, so this defeats its anonymous path. Read the reachability snapshot with a throwaway signer instead (snapshot() only reads; the signer is used solely for writes) — the same trick `graperank status` already uses to stay side-effect-free. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Xc3Wm4qVCrAGvSAotTUVt4 --- .../amethyst/cli/commands/GrapeRankCommand.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/GrapeRankCommand.kt b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/GrapeRankCommand.kt index c6c1e5c1a0..9da16dce86 100644 --- a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/GrapeRankCommand.kt +++ b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/GrapeRankCommand.kt @@ -697,7 +697,12 @@ object GrapeRankCommand { ctx: Context, args: Args, ): Set { - val reach = ctx.reachability.snapshot() + // Read the reachability records (kind:30166) with a throwaway signer, NOT + // ctx.reachability — the latter derives the monitor key and would CREATE the + // operator master (a passphrase prompt) purely to read a cache. The follower + // crawl never signs, so keep it truly account-and-key-free. snapshot() only + // reads; the signer is used solely for writes. Same trick as `status`. + val reach = RelayReachabilityStore(store = ctx.store, signer = NostrSignerInternal(KeyPair())).snapshot() val relays = LinkedHashSet() relays.addAll(reach.live) // Every advertised outbox/inbox relay in the store — the homes of the users