Diagnostics for atproto PDS hosts, DIDs, and handles: https://debug.hose.cam

Compare changes

Choose any two refs to compare.

+222 -97
index.html
··· 33 33 }; 34 34 window.isXrpcErr = e => e instanceof ClientResponseError; 35 35 36 + window.isBeforeNow = iso => new Date(iso) < new Date(); 37 + 36 38 window.dnsResolver = new DohJsonHandleResolver({ 37 39 dohUrl: 'https://mozilla.cloudflare-dns.com/dns-query', 38 40 }); ··· 87 89 hostname: 'relay1.us-west.bsky.network', 88 90 }, 89 91 ]; 92 + 93 + window.regionalModAccounts = [ // https://github.com/mary-ext/atproto-scraping?tab=readme-ov-file#bluesky-labelers 94 + 'https://mod-br.bsky.app', 95 + 'https://mod-de.bsky.app', 96 + 'https://mod-in.bsky.app', 97 + 'https://mod-ru.bsky.app', 98 + 'https://mod-tr.bsky.app', 99 + ]; 100 + 101 + window.bskyAccountDeathLabels = { 102 + ['needs-review']: 'Automated action, cleared by manual review from Bluesky moderation team. Your content can ve accessed via direct links on Bluesky, but invisible in feeds and replies to posts.', 103 + ['!suspend']: 'Moderation action from Bluesky moderation team. Makes your content inaccessible on the Bluesky app.', 104 + ['!takedown']: 'Moderation action from Bluesky moderation team. Makes your content inaccessible on the Bluesky app.', 105 + ['!hide']: 'Almost always used with !takedown, makes your content inaccessible on the Bluesky app.', 106 + // other labels shouldn't cause problems that make you think your pds is broken 107 + // 'spam': just hides replies by default + makes your posts click-through 108 + // 'intolerant', etc: similar to spam 109 + }; 110 + 111 + if (window.blehYeahReady) blehYeahReady(); 112 + else window.yeahBlehIsReady = true; 90 113 </script> 91 114 115 + <style> 116 + body:not(.ready) .hide-until-ready, 117 + body.ready .show-until-ready { 118 + display: none; 119 + } 120 + </style> 121 + 92 122 <script> 93 123 document.addEventListener('alpine:init', () => { 124 + if (window.yeahBlehIsReady) { 125 + document.body.classList.add('ready'); 126 + } else { 127 + window.blehYeahReady = () => document.body.classList.add('ready'); 128 + } 129 + 94 130 Alpine.data('debug', () => ({ 95 131 // form input 96 132 identifier: '', ··· 275 311 }); 276 312 this.status = data.status; 277 313 } catch(e) { 278 - if (relay.missingApis['com.atproto.sync.getHostStatus']) { 314 + if (relay.missingApis?.['com.atproto.sync.getHostStatus']) { 279 315 this.error = 'Can\'t check'; 280 - this.expectedErrorInfo = relay.missingApis['com.atproto.sync.getHostStatus']; 316 + this.expectedErrorInfo = relay.missingApis?.['com.atproto.sync.getHostStatus']; 281 317 } else if (window.isXrpcErr(e)) { 282 318 this.error = e.error; 283 319 } else { ··· 456 492 params: { did }, 457 493 }); 458 494 } catch(e) { 459 - if (relay.missingApis['com.atproto.sync.getRepoStatus']) { 495 + if (relay.missingApis?.['com.atproto.sync.getRepoStatus']) { 460 496 this.error = 'Can\'t check'; 461 - this.expectedErrorInfo = relay.missingApis['com.atproto.sync.getRepoStatus']; 497 + this.expectedErrorInfo = relay.missingApis?.['com.atproto.sync.getRepoStatus']; 462 498 } else if (window.isXrpcErr(e)) { 463 499 this.error = e.error; 464 500 } else { ··· 482 518 } 483 519 })); 484 520 521 + Alpine.data('modLabels', did => ({ 522 + loading: false, 523 + error: null, 524 + regionalErrors: [], 525 + labels: [], 526 + 527 + async init() { 528 + this.loading = true; 529 + this.error = null; 530 + this.regionalErrors = []; 531 + this.labels = []; 532 + 533 + const query = window.SimpleQuery('https://mod.bsky.app'); 534 + 535 + try { 536 + const res = await query('com.atproto.label.queryLabels', { 537 + params: { uriPatterns: [did] }, 538 + }); 539 + this.labels = res.labels ?? []; 540 + // TODO: handle cursors? 541 + 542 + for (const region of window.regionalModAccounts) { 543 + // intentionally no await, these come in async 544 + // (...and could get messy if we start re-checking labels before they're done) 545 + this.checkRegionLabels(region); 546 + } 547 + } catch (e) { 548 + if (window.isXrpcErr(e)) { 549 + this.error = e.error; 550 + } else { 551 + this.error = 'Failed to check (see console)'; 552 + console.error(e); 553 + } 554 + } 555 + this.loading = false; 556 + }, 557 + 558 + async checkRegionLabels(labeler) { 559 + const query = window.SimpleQuery(labeler); 560 + try { 561 + const res = await query('com.atproto.label.queryLabels', { 562 + params: { uriPatterns: [did] }, 563 + }); 564 + if (res?.labels?.length > 0) this.labels.push(...res.labels); 565 + } catch (e) { 566 + if (window.isXrpcErr(e)) { 567 + this.regionalErrors.push(`${labeler}: ${e.error}`); 568 + } else { 569 + this.regionalErrors.push(`Failed to check ${labeler} (see console)`); 570 + console.error(`labeler: ${labeler}`, e); 571 + } 572 + } 573 + } 574 + })); 575 + 485 576 Alpine.data('pdsHistory', (did, currentPds) => ({ 486 577 loading: false, 487 578 error: null, ··· 567 658 this.loading = false; 568 659 }, 569 660 })); 570 - 571 - Alpine.data('modLabels', (did) => ({ 572 - loading: false, 573 - error: null, 574 - labels: [], 575 - 576 - async init() { 577 - this.loading = true; 578 - this.error = null; 579 - this.labels = []; 580 - try { 581 - const res = await fetch(`https://mod.bsky.app/xrpc/com.atproto.label.queryLabels?uriPatterns=${did}`); 582 - if (res.ok) { 583 - const response = await res.json(); 584 - if (response.labels && response.labels.length > 0) { 585 - this.labels = response.labels; 586 - } 587 - } else { 588 - this.error = `${res.status}: ${await res.text()}`; 589 - } 590 - } catch (e) { 591 - this.error = 'failed to get labels from mod.bsky.app'; 592 - console.error(e); 593 - } 594 - this.loading = false; 595 - }, 596 - })); 597 661 }) 598 662 </script> 599 663 </head> ··· 601 665 <div class="hero bg-base-200 p-8"> 602 666 <div class="hero-content flex-col"> 603 667 <h1 class="text-2xl mb-8">PDS Debugger</h1> 604 - <form @submit.prevent="await diagnose()"> 668 + <p class="show-until-ready"><em>Loading&hellip;</em></p> 669 + <form class="hide-until-ready" @submit.prevent="await diagnose()"> 605 670 <label class="text-sm text-primary" for="identifier"> 606 671 atproto handle, DID, or HTTPS PDS URL 607 672 </label> ··· 880 945 ></a> 881 946 </td> 882 947 </tr> 948 + <!--<tr> 949 + <td 950 + class="text-sm" 951 + x-data="repoMonitor(did, pds)" 952 + > 953 + <button 954 + class="btn btn-xs btn-success" 955 + >Start live monitoring</button> 956 + </td> 957 + </tr>--> 883 958 </tbody> 884 959 </table> 885 960 </div> ··· 965 1040 >ahead</span> 966 1041 </td> 967 1042 </template> 1043 + <template x-if="!revStatus(state && state.rev)"> 1044 + <td></td> 1045 + </template> 968 1046 </tr> 969 1047 </template> 970 1048 </tbody> 971 1049 </table> 972 1050 </div> 973 1051 1052 + <div x-data="modLabels(did)"> 1053 + <h3 class="text-lg mt-3"> 1054 + Labels 1055 + </h3> 1056 + <div class="overflow-x-auto"> 1057 + <table class="table table-xs"> 1058 + <tbody> 1059 + <template x-if="loading"> 1060 + <tr> 1061 + <td>Loading&hellip;</td> 1062 + </tr> 1063 + </template> 1064 + <template x-if="error"> 1065 + <tr> 1066 + <td>Error: <span x-text="error"></span></td> 1067 + </tr> 1068 + </template> 1069 + <template x-if="!loading && !error && labels.length === 0"> 1070 + <tr> 1071 + <td class="text-xs"> 1072 + <em>No Bluesky moderation labels found</em> 1073 + </td> 1074 + </tr> 1075 + </template> 1076 + <template x-for="label in labels"> 1077 + <template x-if="!!label"> 1078 + <tr x-data="{ expired: isBeforeNow(label.exp) }"> 1079 + <td> 1080 + <span x-show="label.neg">removed</span> 1081 + <code 1082 + x-text="label.cts.split('T')[0]" 1083 + :title="label.cts" 1084 + ></code> 1085 + </td> 1086 + <td> 1087 + <template x-if="!!label.exp"> 1088 + <span x-text="expired ? 'expired' : 'expires'"></span> 1089 + <code 1090 + x-text="label.exp.split('T')[0]" 1091 + :title="label.exp" 1092 + ></code> 1093 + </template> 1094 + </td> 1095 + <td> 1096 + <code 1097 + x-text="label.val" 1098 + class="badge badge-sm badge-soft" 1099 + :class="(label.neg || expired) 1100 + ? 'badge-neutral line-through' 1101 + : !!window.bskyAccountDeathLabels[label.val] 1102 + ? 'badge-warning' 1103 + : 'badge-info'" 1104 + :title="label.neg 1105 + ? 'label negated' 1106 + : expired 1107 + ? 'label expired' 1108 + : window.bskyAccountDeathLabels[label.val] ?? ''" 1109 + ></code> 1110 + </td> 1111 + <td 1112 + x-data="didToHandle(label.src)" 1113 + x-intersect:enter.once="load" 1114 + > 1115 + <span x-show="loading">Loading&hellip;</span> 1116 + <span x-show="error !== null" x-text="error"></span> 1117 + <a 1118 + href="#" 1119 + class="link" 1120 + @click.prevent="goto(handle)" 1121 + x-show="handle !== null" 1122 + x-text="`@${handle}`" 1123 + ></a> 1124 + </td> 1125 + </tr> 1126 + </template> 1127 + </template> 1128 + </tbody> 1129 + </table> 1130 + </div> 1131 + <template x-for="error in regionalErrors"> 1132 + <p 1133 + x-text="error" 1134 + class="text-xs text-warning" 1135 + ></p> 1136 + </template> 1137 + </div> 1138 + 974 1139 <template x-if="did.startsWith('did:plc:')"> 975 1140 <div x-data="pdsHistory(did, pds)"> 976 1141 <h3 class="text-lg mt-3"> ··· 1042 1207 </div> 1043 1208 </div> 1044 1209 </template> 1045 - 1046 - <template x-if="did != null"> 1047 - <div x-data="modLabels(did)"> 1048 - <h3 class="text-lg mt-3"> 1049 - Labels from Bluesky (mod.bsky.app) 1050 - </h3> 1051 - <div class="overflow-x-auto"> 1052 - <table class="table table-xs"> 1053 - <template x-if="!loading && !error && labels.length !== 0"> 1054 - <thead> 1055 - <tr> 1056 - <th>created</th> 1057 - <th>exp</th> 1058 - <th>label</th> 1059 - </tr> 1060 - </thead> 1061 - </template> 1062 - <tbody> 1063 - <template x-if="loading"> 1064 - <tr> 1065 - <td>Loading&hellip;</td> 1066 - </tr> 1067 - </template> 1068 - <template x-if="error"> 1069 - <tr> 1070 - <td>Error: <span x-text="error"></span></td> 1071 - </tr> 1072 - </template> 1073 - <template x-if="!loading && !error && labels.length === 0"> 1074 - <tr> 1075 - <td class="text-sm"> 1076 - <em>no labels applied by mod.bsky.app</em> 1077 - </td> 1078 - </tr> 1079 - </template> 1080 - <template x-for="label in labels"> 1081 - <tr> 1082 - <td> 1083 - <code x-text="label.cts.split('.')[0]"></code> 1084 - </td> 1085 - <td> 1086 - <code x-text="label.exp.split('.')[0]"></code> 1087 - </td> 1088 - 1089 - <td> 1090 - <span 1091 - x-text="label.val" 1092 - class="badge badge-sm badge-soft badge-warning" 1093 - ></span> 1094 - </td> 1095 - 1096 - </tr> 1097 - </template> 1098 - </tbody> 1099 - </table> 1100 - </div> 1101 - </div> 1102 - </template> 1103 1210 </div> 1104 1211 </template> 1105 1212 </div> ··· 1211 1318 1212 1319 <div class="footer text-xs sm:footer-horizontal text-neutral mt-32 p-8 max-w-2xl mx-auto"> 1213 1320 <nav> 1214 - <h3 class="footer-title">Current limitations</h3> 1215 - <p>PDS hosts without CORS will fail tests</p> 1216 - <p>Bluesky relay is missing API endpoints</p> 1217 - <p>Blacksky relay is also missing API endpoints</p> 1218 - <p>The requestCrawl button is not well tested</p> 1321 + <h3 class="footer-title mt-3">Current limitations</h3> 1322 + <p>PDS hosts without CORS will fail tests.</p> 1323 + <p>Bluesky relay is missing API endpoints.</p> 1324 + <p>Blacksky relay is also missing API endpoints.</p> 1325 + <p>The requestCrawl button is not well tested.</p> 1326 + 1327 + <h3 class="footer-title mt-3">Future features</h3> 1328 + <p>Firehose listener</p> 1329 + <p>URL routing</p> 1330 + <p>Less strict identity resolution</p> 1219 1331 </nav> 1332 + 1220 1333 <nav> 1334 + <h3 class="footer-title mt-3">Places</h3> 1335 + <p><a href="https://tangled.org/microcosm.blue/pds-debug">Source code (tangled.org)</a></p> 1336 + <p><a href="https://discord.gg/Vwamex5UFS">Discord (microcosm)</a></p> 1337 + <p><a href="https://pdsmoover.com/">PDS Moover</a></p> 1338 + <p><a href="https://microcosm.blue">microcosm</a></p> 1221 1339 1222 - <h3 class="footer-title">Future features</h3> 1223 - <p>Account label lookup</p> 1340 + <h3 class="footer-title mt-3">Made by</h3> 1341 + <p> 1342 + <a href="https://bsky.app/profile/did:plc:hdhoaan3xa3jiuq4fg4mefid">fig</a> 1343 + <a href="https://github.com/sponsors/uniphil">(sponsor)</a> 1344 + </p> 1345 + <p> 1346 + <a href="https://bsky.app/profile/did:plc:rnpkyqnmsw4ipey6eotbdnnf">bailey</a> 1347 + <a href="https://github.com/sponsors/fatfingers23">(sponsor)</a> 1348 + </p> 1224 1349 </nav> 1225 1350 </div> 1226 1351
+5
readme.md
··· 1 + # PDS Debugger 2 + 3 + https://debug.hose.cam 4 + 5 + Diagnostics for atproto PDS hosts, DIDs, and handles
+13
useful-accounts.txt
··· 1 + some accounts that show things useful for testing the debugger 2 + 3 + 4 + Labels 5 + 6 + - did:plc:bnwrgnvwkg2n5cbvk4xodb3h | !hide | no other labels 7 + - did:plc:qhl3vg5tmwey536z2fil2lrh | !hide | from moderation-tr.bsky.app 8 + - did:plc:fsmaoqqnm6knqh4cuphb4jow | !hide, ~!takedown | takedown negated 9 + - did:plc:iv3yod6zf2j4zaakq6qyiz46 | !takedown | 10 + - did:plc:nwrcwcrhpkgrqqvkg3lmaqky | ~needs-review, ~!takedown | both negated 11 + - did:plc:2tinwgqvf4asiwh36ii6ko7l | needs-review | expired 12 + - did:plc:5plqrpw3x6j5wzaosssqams7 | spam | no other labels 13 + - did:plc:tqww7jdpqx5tb3w435fugmxi | intolerant |