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

Compare changes

Choose any two refs to compare.

+733 -499
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: '', ··· 143 179 } 144 180 } 145 181 } else { 146 - this.handle = this.identifier; 182 + this.handle = this.identifier.toLowerCase(); 147 183 let data; 148 184 try { 149 185 data = await window.slingshot('com.bad-example.identity.resolveMiniDoc', { 150 - params: { identifier: this.identifier }, 186 + params: { identifier: this.identifier.toLowerCase() }, 151 187 }); 152 188 this.did = data.did; 153 189 this.pds = data.pds; ··· 220 256 params: { limit: 100 }, 221 257 }); 222 258 this.accounts = accountsRes.repos; 223 - this.accountsComplete == !accountsRes.cursor; 259 + 260 + // weird thing with the ref pds: it *always* has a cursor on the first page 261 + if (accountsRes.cursor) { 262 + // so grab a second page just to see if there really is a second page 263 + try { 264 + const secondPage = await query('com.atproto.sync.listRepos', { 265 + params: { limit: 1, cursor: accountsRes.cursor }, 266 + }); 267 + this.accountsComplete = !secondPage.cursor || secondPage.repos.length == 0; 268 + } catch (e) { 269 + // we're in a niche spot. ignore errors and look at the original (faulty) cursor 270 + this.accountsComplete = !accountsRes.cursor; // ๐Ÿคทโ€โ™€๏ธ 271 + } 272 + } else { 273 + this.accountsComplete = true; 274 + } 275 + 224 276 } catch (e) { 225 277 if (window.isXrpcErr(e)) { 226 278 this.error = e.error; ··· 259 311 }); 260 312 this.status = data.status; 261 313 } catch(e) { 262 - if (relay.missingApis['com.atproto.sync.getHostStatus']) { 314 + if (relay.missingApis?.['com.atproto.sync.getHostStatus']) { 263 315 this.error = 'Can\'t check'; 264 - this.expectedErrorInfo = relay.missingApis['com.atproto.sync.getHostStatus']; 316 + this.expectedErrorInfo = relay.missingApis?.['com.atproto.sync.getHostStatus']; 265 317 } else if (window.isXrpcErr(e)) { 266 318 this.error = e.error; 267 319 } else { ··· 440 492 params: { did }, 441 493 }); 442 494 } catch(e) { 443 - if (relay.missingApis['com.atproto.sync.getRepoStatus']) { 495 + if (relay.missingApis?.['com.atproto.sync.getRepoStatus']) { 444 496 this.error = 'Can\'t check'; 445 - this.expectedErrorInfo = relay.missingApis['com.atproto.sync.getRepoStatus']; 497 + this.expectedErrorInfo = relay.missingApis?.['com.atproto.sync.getRepoStatus']; 446 498 } else if (window.isXrpcErr(e)) { 447 499 this.error = e.error; 448 500 } else { ··· 466 518 } 467 519 })); 468 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 + 469 576 Alpine.data('pdsHistory', (did, currentPds) => ({ 470 577 loading: false, 471 578 error: null, ··· 554 661 }) 555 662 </script> 556 663 </head> 557 - <body x-data="debug"> 558 - <div class="hero bg-base-200"> 664 + <body x-data="debug" class="bg-base-200"> 665 + <div class="hero bg-base-200 p-8"> 559 666 <div class="hero-content flex-col"> 560 - <h1>PDS Debugger</h1> 561 - 562 - <p class="text-sm">Work in progress!</p> 563 - <details class="text-xs"> 564 - <summary>Future features</summary> 565 - <ul class="list-disc pl-4"> 566 - <li>firehose & jetstream listeners</li> 567 - </ul> 568 - </details> 569 - <details class="text-xs"> 570 - <summary>Limitations</summary> 571 - <ul class="list-disc pl-4"> 572 - <li>The Bluesky production relay at <code>bsky.network</code> runs the old bgs implementation, and is missing many relay XRPC endpoints.</li> 573 - <li>The Blacksky relay at <code>atproto.africa</code> runs an independent implementation, and is also missing many relay XRPC endpoints.</li> 574 - <li>All diagnostics run in your browser, so servers that don't enable CORS (some PDS hosts, Upcloud's relay) will fail tests.</li> 575 - </ul> 576 - </details> 577 - 578 - <div class="card bg-base-100 w-full max-w-sm shrink-0 shadow-2xl"> 579 - <div class="card-body"> 580 - <form @submit.prevent="await diagnose()"> 581 - <label> 582 - Enter an atproto handle, DID, or HTTPS PDS URL 583 - <input 584 - class="input" 585 - x-model="identifier" 586 - :disabled="identifierLoading" 587 - autofocus 588 - /> 589 - </label> 590 - </form> 667 + <h1 class="text-2xl mb-8">PDS Debugger</h1> 668 + <p class="show-until-ready"><em>Loading&hellip;</em></p> 669 + <form class="hide-until-ready" @submit.prevent="await diagnose()"> 670 + <label class="text-sm text-primary" for="identifier"> 671 + atproto handle, DID, or HTTPS PDS URL 672 + </label> 673 + <br/> 674 + <div class="join"> 675 + <input 676 + id="identifier" 677 + class="input join-item" 678 + x-model="identifier" 679 + :disabled="identifierLoading" 680 + autofocus 681 + /> 682 + <button 683 + class="btn btn-primary join-item" 684 + type="submit" 685 + >go</button> 591 686 </div> 592 - </div> 687 + </form> 688 + </div> 689 + </div> 593 690 594 - <template x-if="identifierError"> 595 - <p>uh oh: <span x-text="identifierError"></span></p> 596 - </template> 691 + <div class="w-full max-w-lg mx-auto"> 692 + <template x-if="identifierError"> 693 + <p>uh oh: <span x-text="identifierError"></span></p> 694 + </template> 597 695 598 - <template x-if="pds != null"> 599 - <div class="card bg-base-100 w-full max-w-lg shrink-0 shadow-2xl"> 600 - <div class="card-body"> 601 - <h2 class="card-title"> 602 - <span class="badge badge-secondary">PDS</span> 603 - <span x-text="pds"></span> 604 - </h2> 605 - 606 - <div 607 - x-data="pdsCheck(pds)" 608 - x-init="$watch('pds', v => update(v))" 609 - > 610 - <h3 class="text-lg mt-3"> 611 - Server 612 - <span 613 - x-show="description !== null" 614 - class="badge badge-sm badge-soft badge-success" 615 - >online</span> 616 - </h3> 617 - <p x-show="loadingDesc">Loading&hellip;</p> 618 - <p x-show="error" class="text-warning" x-text="error"></p> 619 - <template x-if="description !== null"> 620 - <div> 621 - <div class="overflow-x-auto"> 622 - <table class="table table-xs"> 623 - <tbody> 624 - <tr> 625 - <td class="text-sm"> 626 - Open registration: 627 - <span 628 - x-text="!description.inviteCodeRequired" 629 - ></span> 630 - </td> 631 - </tr> 632 - <tr> 633 - <td class="text-sm"> 634 - Version: 635 - <code 636 - class="text-xs" 637 - x-text="version" 638 - ></code> 639 - </td> 640 - </tr> 641 - </tbody> 642 - </table> 643 - </div> 644 - <h4 class="font-bold"> 645 - Accounts 646 - </h4> 647 - <div class="overflow-x-auto overflow-y-auto max-h-26"> 648 - <table class="table table-xs"> 649 - <tbody> 650 - <template x-for="account in accounts"> 651 - <tr> 652 - <td> 653 - <code> 654 - <a 655 - href="#" 656 - class="link" 657 - x-text="account.did" 658 - @click.prevent="goto(account.did)" 659 - ></a> 660 - </code> 661 - </td> 662 - <td> 663 - <span 664 - x-show="account.active" 665 - class="badge badge-sm badge-soft badge-success" 666 - > 667 - active 668 - </span> 669 - <span 670 - x-show="!account.active" 671 - x-text="account.status" 672 - class="badge badge-sm badge-soft badge-warning" 673 - ></span> 674 - </td> 675 - <td 676 - x-data="didToHandle(account.did)" 677 - x-intersect:enter.once="load" 678 - > 679 - <span x-show="loading">Loading&hellip;</span> 680 - <span x-show="error !== null" x-text="error"></span> 681 - <a 682 - href="#" 683 - class="link" 684 - @click.prevent="goto(handle)" 685 - x-show="handle !== null" 686 - x-text="`@${handle}`" 687 - ></a> 688 - </td> 689 - </tr> 690 - </template> 691 - <template x-if="!loadingDesc && !accountsComplete"> 692 - <tr> 693 - <td colspan="2" class="text-xs text-warning-content"> 694 - (more accounts not shown) 695 - </td> 696 - </tr> 697 - </template> 698 - </tbody> 699 - </table> 700 - </div> 701 - </div> 702 - </template> 703 - </div> 696 + <template x-if="pds != null"> 704 697 705 - <h3 class="text-lg mt-3">Relay host status</h3> 706 - <div class="overflow-x-auto"> 707 - <table class="table table-xs"> 708 - <tbody> 709 - <template x-for="relay in window.relays"> 710 - <tr 711 - x-data="relayCheckHost(pds, relay)" 712 - x-init="$watch('pds', pds => check(pds, relay))" 713 - > 714 - <td class="text-sm"> 715 - <div class="tooltip tooltip-right" :data-tip="relay.hostname"> 716 - <img 717 - class="inline-block h-4 w-4" 718 - :src="relay.icon" 719 - alt="" 720 - /> 721 - <span x-text="relay.name"></span> 722 - <span 723 - x-show="!!relay.note" 724 - x-text="relay.note" 725 - class="badge badge-soft badge-neutral badge-xs" 726 - ></span> 727 - </div> 728 - </td> 729 - <td> 730 - <template x-if="loading"> 731 - <em>loading&hellip;</em> 732 - </template> 733 - <template x-if="error"> 734 - <div 735 - class="text-xs" 736 - :class="expectedErrorInfo 737 - ? 'text-info tooltip tooltip-left cursor-help' 738 - : 'text-warning'" 739 - :data-tip="expectedErrorInfo" 740 - > 741 - <span x-text="error"></span> 742 - <span 743 - x-show="!!expectedErrorInfo" 744 - class="badge badge-soft badge-info badge-xs" 745 - >i</span> 746 - </div> 747 - </template> 748 - <template x-if="status"> 749 - <span 750 - x-text="status" 751 - class="badge badge-sm" 752 - :class="status === 'active' && 'badge-soft badge-success'" 753 - ></span> 754 - </template> 755 - </td> 756 - <td> 757 - <div x-show="status !== 'active' && !expectedErrorInfo"> 758 - <button 759 - x-show="reqCrawlStatus !== 'done'" 760 - class="btn btn-xs btn-ghost whitespace-nowrap" 761 - :disabled="reqCrawlStatus === 'loading'" 762 - @click="requestCrawl(pds, relay)" 763 - > 764 - request crawl 765 - </button> 766 - <span 767 - x-show="reqCrawlError !== null" 768 - x-text="reqCrawlError" 769 - class="text-xs text-warning" 770 - ></span> 771 - <button 772 - x-show="reqCrawlError === null && reqCrawlStatus === 'done'" 773 - class="btn btn-xs btn-soft btn-primary whitespace-nowrap" 774 - @click="check" 775 - > 776 - refresh 777 - </button> 778 - </div> 779 - </td> 780 - </tr> 781 - </template> 782 - </tbody> 783 - </table> 784 - </div> 785 - </div> 786 - </div> 787 - </template> 698 + <div class="card bg-base-100 w-full max-w-2xl shrink-0 shadow-2xl m-4"> 699 + <div class="card-body"> 700 + <h2 class="card-title"> 701 + <span class="badge badge-secondary">PDS</span> 702 + <span x-text="pds"></span> 703 + </h2> 788 704 789 - <template x-if="did != null"> 790 - <div class="card bg-base-100 w-full max-w-lg shrink-0 shadow-2xl"> 791 - <div class="card-body"> 792 - <h2 class="card-title"> 793 - <span class="badge badge-secondary">DID</span> 794 - <code x-text="did"></code> 795 - </h2> 796 - <template x-if="pds != null"> 797 - <div x-data="didRepoState(did, pds)"> 798 - <h3 class="text-lg mt-3"> 799 - Repo 800 - <span 801 - x-show="state && state.active" 802 - class="badge badge-sm badge-soft badge-success" 803 - >active</span> 804 - </h3> 705 + <div 706 + x-data="pdsCheck(pds)" 707 + x-init="$watch('pds', v => update(v))" 708 + > 709 + <h3 class="text-lg mt-3"> 710 + Server 711 + <span 712 + x-show="description !== null" 713 + class="badge badge-sm badge-soft badge-success" 714 + >online</span> 715 + </h3> 716 + <p x-show="loadingDesc">Loading&hellip;</p> 717 + <p x-show="error" class="text-warning" x-text="error"></p> 718 + <template x-if="description !== null"> 719 + <div> 805 720 <div class="overflow-x-auto"> 806 721 <table class="table table-xs"> 807 722 <tbody> 808 723 <tr> 809 724 <td class="text-sm"> 810 - Rev: 811 - <code x-text="state && state.rev"></code> 725 + Open registration: 726 + <span 727 + x-text="!description.inviteCodeRequired" 728 + ></span> 812 729 </td> 813 730 </tr> 814 731 <tr> 815 732 <td class="text-sm"> 816 - PDS: 817 - <a 818 - href="#" 819 - class="link" 820 - @click.prevent="goto(pds)" 821 - x-text="pds" 822 - ></a> 823 - </td> 824 - </tr> 825 - <tr> 826 - <td class="text-sm"> 827 - Size: 828 - <span x-data="repoSize"> 829 - <template x-if="loading"> 830 - <em>loading&hellip;</em> 831 - </template> 832 - <template x-if="error"> 833 - <span class="text-xs text-warning" x-text="error"></span> 834 - </template> 835 - <template x-if="size"> 836 - <code> 837 - <span x-text="size"></span> MiB 838 - </code> 839 - </template> 840 - <template x-if="!size && !error && !loading"> 841 - <button 842 - class="btn btn-xs btn-soft btn-primary" 843 - @click.prevent="loadRepoForSize(did, pds)" 844 - >load</button> 845 - </template> 846 - </span> 733 + Version: 734 + <code 735 + class="text-xs" 736 + x-text="version" 737 + ></code> 847 738 </td> 848 739 </tr> 849 740 </tbody> 850 741 </table> 851 742 </div> 852 - 853 - <h3 class="text-lg mt-3"> 854 - Relay repo status 855 - </h3> 856 - <div class="overflow-x-auto"> 743 + <h4 class="font-bold"> 744 + Accounts 745 + </h4> 746 + <div class="overflow-x-auto overflow-y-auto max-h-26"> 857 747 <table class="table table-xs"> 858 748 <tbody> 859 - <template x-for="relay in window.relays"> 860 - <tr 861 - x-data="relayCheckRepo(did, relay)" 862 - x-init="$watch('pds', pds => check(did, relay))" 863 - > 864 - <td class="text-sm"> 865 - <div class="tooltip tooltip-right" :data-tip="relay.hostname"> 866 - <img 867 - class="inline-block h-4 w-4" 868 - :src="relay.icon" 869 - alt="" 870 - /> 871 - <span x-text="relay.name"></span> 872 - <span 873 - x-show="!!relay.note" 874 - x-text="relay.note" 875 - class="badge badge-neutral badge-soft badge-xs" 876 - ></span> 877 - </div> 749 + <template x-for="account in accounts"> 750 + <tr> 751 + <td> 752 + <code> 753 + <a 754 + href="#" 755 + class="link" 756 + x-text="account.did" 757 + @click.prevent="goto(account.did)" 758 + ></a> 759 + </code> 878 760 </td> 879 - <template x-if="loading"> 880 - <td> 881 - <em>loading&hellip;</em> 882 - </td> 883 - </template> 884 - <template x-if="error"> 885 - <td> 886 - <div 887 - class="text-xs" 888 - :class="expectedErrorInfo 889 - ? 'text-info tooltip tooltip-left cursor-help' 890 - : 'text-warning'" 891 - :data-tip="expectedErrorInfo" 892 - > 893 - <span x-text="error"></span> 894 - <span 895 - x-show="!!expectedErrorInfo" 896 - class="badge badge-soft badge-info badge-xs" 897 - >i</span> 898 - </div> 899 - </td> 900 - </template> 901 - <template x-if="status"> 902 - <td> 903 - <span 904 - x-show="status.active" 905 - class="badge badge-sm badge-soft badge-success" 906 - > 907 - active 908 - </span> 909 - <span 910 - x-show="!status.active" 911 - x-text="status.status" 912 - class="badge badge-sm badge-soft badge-warning" 913 - ></span> 914 - </td> 915 - </template> 916 - <template x-if="revStatus(state && state.rev)"> 917 - <td x-data="{ asdf: revStatus(state.rev) }"> 918 - <span 919 - x-show="asdf === 'current'" 920 - class="badge badge-sm badge-soft badge-success" 921 - >current</span> 922 - <span 923 - x-show="asdf === 'behind'" 924 - class="badge badge-sm badge-soft badge-warning tooltip tooltip-left" 925 - :data-tip="status.rev" 926 - >behind</span> 927 - <span 928 - x-show="asdf === 'ahead'" 929 - class="badge badge-sm badge-soft badge-success tooltip tooltip-left" 930 - :data-tip="`Account may have updated between checks? ${status.rev}`" 931 - >ahead</span> 932 - </td> 933 - </template> 761 + <td> 762 + <span 763 + x-show="account.active" 764 + class="badge badge-sm badge-soft badge-success" 765 + > 766 + active 767 + </span> 768 + <span 769 + x-show="!account.active" 770 + x-text="account.status" 771 + class="badge badge-sm badge-soft badge-warning" 772 + ></span> 773 + </td> 774 + <td 775 + x-data="didToHandle(account.did)" 776 + x-intersect:enter.once="load" 777 + > 778 + <span x-show="loading">Loading&hellip;</span> 779 + <span x-show="error !== null" x-text="error"></span> 780 + <a 781 + href="#" 782 + class="link" 783 + @click.prevent="goto(handle)" 784 + x-show="handle !== null" 785 + x-text="`@${handle}`" 786 + ></a> 787 + </td> 788 + </tr> 789 + </template> 790 + <template x-if="!loadingDesc && !accountsComplete"> 791 + <tr> 792 + <td colspan="2" class="text-xs text-warning-content"> 793 + (more accounts not shown) 794 + </td> 934 795 </tr> 935 796 </template> 936 797 </tbody> 937 798 </table> 938 799 </div> 939 - 940 - <template x-if="did.startsWith('did:plc:')"> 941 - <div x-data="pdsHistory(did, pds)"> 942 - <h3 class="text-lg mt-3"> 943 - PLC PDS history 944 - </h3> 945 - <div class="overflow-x-auto"> 946 - <table class="table table-xs"> 947 - <tbody> 948 - <template x-if="loading"> 949 - <tr> 950 - <td>Loading&hellip;</td> 951 - </tr> 952 - </template> 953 - <template x-if="error"> 954 - <tr> 955 - <td>Error: <span x-text="error"></span></td> 956 - </tr> 957 - </template> 958 - <template x-if="!loading && !error && history.length === 0"> 959 - <tr> 960 - <td class="text-sm"> 961 - <em>no previous PDS</em> 962 - </td> 963 - </tr> 964 - </template> 965 - <template x-for="event in history"> 966 - <tr x-data="didRepoState(did, event.pds)"> 967 - <td> 968 - <code x-text="event.date.split('T')[0]"></code> 969 - </td> 970 - <td> 971 - <a 972 - href="#" 973 - class="link" 974 - @click.prevent="goto(event.pds)" 975 - x-text="event.pds" 976 - ></a> 977 - </td> 978 - <template x-if="event.current"> 979 - <td> 980 - <span 981 - x-show="state && !state.active" 982 - x-text="state && state.status" 983 - class="badge badge-sm badge-soft badge-warning" 984 - ></span> 985 - <span 986 - x-show="state && state.active" 987 - class="badge badge-sm badge-soft badge-success" 988 - >current</span> 989 - </td> 990 - </template> 991 - <template x-if="!event.current"> 992 - <td> 993 - <span 994 - x-show="state && !state.active" 995 - x-text="state && state.status" 996 - class="badge badge-sm badge-soft badge-success" 997 - ></span> 998 - <span 999 - x-show="state && state.active" 1000 - class="badge badge-sm badge-soft badge-warning" 1001 - >active</span> 1002 - </td> 1003 - </template> 1004 - </tr> 1005 - </template> 1006 - </tbody> 1007 - </table> 1008 - </div> 1009 - </div> 1010 - </template> 1011 800 </div> 1012 801 </template> 1013 802 </div> 1014 - </div> 1015 - </template> 1016 803 1017 - <template x-if="handle !== null"> 1018 - <div class="card bg-base-100 w-full max-w-lg shrink-0 shadow-2xl"> 1019 - <div 1020 - x-data="checkHandle(handle)" 1021 - x-init="$watch('handle', h => updateHandle(h))" 1022 - class="card-body" 1023 - > 1024 - <h2 class="card-title"> 1025 - <span class="badge badge-secondary">Handle</span> 1026 - <span x-text="handle"></span> 1027 - </h2> 1028 - 1029 - <h3 class="text-lg mt-3"> 1030 - Resolution 1031 - </h3> 1032 - <p x-show="loading" class="text-i">Loading&hellip;</p> 1033 - <div x-show="!loading" class="overflow-x-auto"> 1034 - <table class="table table-xs"> 1035 - <tbody> 1036 - <tr> 1037 - <td class="text-sm">DNS</td> 804 + <h3 class="text-lg mt-3">Relay host status</h3> 805 + <div class="overflow-x-auto"> 806 + <table class="table table-xs"> 807 + <tbody> 808 + <template x-for="relay in window.relays"> 809 + <tr 810 + x-data="relayCheckHost(pds, relay)" 811 + x-init="$watch('pds', pds => check(pds, relay))" 812 + > 1038 813 <td class="text-sm"> 1039 - <code x-text="dnsDid"></code> 814 + <div class="tooltip tooltip-right" :data-tip="relay.hostname"> 815 + <img 816 + class="inline-block h-4 w-4" 817 + :src="relay.icon" 818 + alt="" 819 + /> 820 + <span x-text="relay.name"></span> 821 + <span 822 + x-show="!!relay.note" 823 + x-text="relay.note" 824 + class="badge badge-soft badge-neutral badge-xs" 825 + ></span> 826 + </div> 1040 827 </td> 1041 828 <td> 1042 - <div 1043 - class="badge badge-sm badge-soft badge-neutral" 1044 - x-show="dnsErr !== null" 1045 - x-text="dnsErr" 1046 - ></div> 1047 - </td> 1048 - </tr> 1049 - <tr> 1050 - <td class="text-sm">Http</td> 1051 - <td class="text-sm"> 1052 - <code x-text="httpDid"></code> 829 + <template x-if="loading"> 830 + <em>loading&hellip;</em> 831 + </template> 832 + <template x-if="error"> 833 + <div 834 + class="text-xs" 835 + :class="expectedErrorInfo 836 + ? 'text-info tooltip tooltip-left cursor-help' 837 + : 'text-warning'" 838 + :data-tip="expectedErrorInfo" 839 + > 840 + <span x-text="error"></span> 841 + <span 842 + x-show="!!expectedErrorInfo" 843 + class="badge badge-soft badge-info badge-xs" 844 + >i</span> 845 + </div> 846 + </template> 847 + <template x-if="status"> 848 + <span 849 + x-text="status" 850 + class="badge badge-sm" 851 + :class="status === 'active' && 'badge-soft badge-success'" 852 + ></span> 853 + </template> 1053 854 </td> 1054 855 <td> 1055 - <div 1056 - class="badge badge-sm badge-soft badge-neutral" 1057 - x-show="httpErr !== null" 1058 - x-text="httpErr" 1059 - ></div> 856 + <div x-show="status !== 'active' && !expectedErrorInfo"> 857 + <button 858 + x-show="reqCrawlStatus !== 'done'" 859 + class="btn btn-xs btn-ghost whitespace-nowrap" 860 + :disabled="reqCrawlStatus === 'loading'" 861 + @click="requestCrawl(pds, relay)" 862 + > 863 + request crawl 864 + </button> 865 + <span 866 + x-show="reqCrawlError !== null" 867 + x-text="reqCrawlError" 868 + class="text-xs text-warning" 869 + ></span> 870 + <button 871 + x-show="reqCrawlError === null && reqCrawlStatus === 'done'" 872 + class="btn btn-xs btn-soft btn-primary whitespace-nowrap" 873 + @click="check" 874 + > 875 + refresh 876 + </button> 877 + </div> 1060 878 </td> 1061 879 </tr> 1062 - </tbody> 1063 - </table> 1064 - </div> 880 + </template> 881 + </tbody> 882 + </table> 883 + </div> 884 + </div> 885 + </div> 886 + </template> 1065 887 1066 - <template x-if="did !== null && did.startsWith('did:plc:')"> 888 + <template x-if="did != null"> 889 + <div class="card bg-base-100 w-full max-w-2xl shrink-0 shadow-2xl m-4"> 890 + <div class="card-body"> 891 + <h2 class="card-title"> 892 + <span class="badge badge-secondary">DID</span> 893 + <code x-text="did"></code> 894 + </h2> 895 + <template x-if="pds != null"> 896 + <div x-data="didRepoState(did, pds)"> 897 + <h3 class="text-lg mt-3"> 898 + Repo 899 + <span 900 + x-show="state && state.active" 901 + class="badge badge-sm badge-soft badge-success" 902 + >active</span> 903 + </h3> 904 + <div class="overflow-x-auto"> 905 + <table class="table table-xs"> 906 + <tbody> 907 + <tr> 908 + <td class="text-sm"> 909 + Rev: 910 + <code x-text="state && state.rev"></code> 911 + </td> 912 + </tr> 913 + <tr> 914 + <td class="text-sm"> 915 + Size: 916 + <span x-data="repoSize"> 917 + <template x-if="loading"> 918 + <em>loading&hellip;</em> 919 + </template> 920 + <template x-if="error"> 921 + <span class="text-xs text-warning" x-text="error"></span> 922 + </template> 923 + <template x-if="size"> 924 + <code> 925 + <span x-text="size"></span> MiB 926 + </code> 927 + </template> 928 + <template x-if="!size && !error && !loading"> 929 + <button 930 + class="btn btn-xs btn-soft btn-primary" 931 + @click.prevent="loadRepoForSize(did, pds)" 932 + >load</button> 933 + </template> 934 + </span> 935 + </td> 936 + </tr> 937 + <tr> 938 + <td class="text-sm"> 939 + PDS: 940 + <a 941 + href="#" 942 + class="link" 943 + @click.prevent="goto(pds)" 944 + x-text="pds" 945 + ></a> 946 + </td> 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>--> 958 + </tbody> 959 + </table> 960 + </div> 1067 961 1068 - <div x-data="handleHistory(did, handle)"> 962 + <h3 class="text-lg mt-3"> 963 + Relay repo status 964 + </h3> 965 + <div class="overflow-x-auto"> 966 + <table class="table table-xs"> 967 + <tbody> 968 + <template x-for="relay in window.relays"> 969 + <tr 970 + x-data="relayCheckRepo(did, relay)" 971 + x-init="$watch('pds', pds => check(did, relay))" 972 + > 973 + <td class="text-sm"> 974 + <div class="tooltip tooltip-right" :data-tip="relay.hostname"> 975 + <img 976 + class="inline-block h-4 w-4" 977 + :src="relay.icon" 978 + alt="" 979 + /> 980 + <span x-text="relay.name"></span> 981 + <span 982 + x-show="!!relay.note" 983 + x-text="relay.note" 984 + class="badge badge-neutral badge-soft badge-xs" 985 + ></span> 986 + </div> 987 + </td> 988 + <template x-if="loading"> 989 + <td> 990 + <em>loading&hellip;</em> 991 + </td> 992 + </template> 993 + <template x-if="error"> 994 + <td> 995 + <div 996 + class="text-xs" 997 + :class="expectedErrorInfo 998 + ? 'text-info tooltip tooltip-left cursor-help' 999 + : 'text-warning'" 1000 + :data-tip="expectedErrorInfo" 1001 + > 1002 + <span x-text="error"></span> 1003 + <span 1004 + x-show="!!expectedErrorInfo" 1005 + class="badge badge-soft badge-info badge-xs" 1006 + >i</span> 1007 + </div> 1008 + </td> 1009 + </template> 1010 + <template x-if="status"> 1011 + <td> 1012 + <span 1013 + x-show="status.active" 1014 + class="badge badge-sm badge-soft badge-success" 1015 + > 1016 + active 1017 + </span> 1018 + <span 1019 + x-show="!status.active" 1020 + x-text="status.status" 1021 + class="badge badge-sm badge-soft badge-warning" 1022 + ></span> 1023 + </td> 1024 + </template> 1025 + <template x-if="revStatus(state && state.rev)"> 1026 + <td x-data="{ asdf: revStatus(state.rev) }"> 1027 + <span 1028 + x-show="asdf === 'current'" 1029 + class="badge badge-sm badge-soft badge-success" 1030 + >current</span> 1031 + <span 1032 + x-show="asdf === 'behind'" 1033 + class="badge badge-sm badge-soft badge-warning tooltip tooltip-left" 1034 + :data-tip="status.rev" 1035 + >behind</span> 1036 + <span 1037 + x-show="asdf === 'ahead'" 1038 + class="badge badge-sm badge-soft badge-success tooltip tooltip-left" 1039 + :data-tip="`Account may have updated between checks? ${status.rev}`" 1040 + >ahead</span> 1041 + </td> 1042 + </template> 1043 + <template x-if="!revStatus(state && state.rev)"> 1044 + <td></td> 1045 + </template> 1046 + </tr> 1047 + </template> 1048 + </tbody> 1049 + </table> 1050 + </div> 1051 + 1052 + <div x-data="modLabels(did)"> 1069 1053 <h3 class="text-lg mt-3"> 1070 - PLC handle history 1054 + Labels 1071 1055 </h3> 1072 1056 <div class="overflow-x-auto"> 1073 1057 <table class="table table-xs"> ··· 1082 1066 <td>Error: <span x-text="error"></span></td> 1083 1067 </tr> 1084 1068 </template> 1085 - <template x-if="!loading && !error && history.length === 0"> 1069 + <template x-if="!loading && !error && labels.length === 0"> 1086 1070 <tr> 1087 - <td class="text-sm"> 1088 - <em>no previous handle</em> 1071 + <td class="text-xs"> 1072 + <em>No Bluesky moderation labels found</em> 1089 1073 </td> 1090 1074 </tr> 1091 1075 </template> 1092 - <template x-for="event in history"> 1093 - <tr> 1094 - <td> 1095 - <code x-text="event.date.split('T')[0]"></code> 1096 - </td> 1097 - <td> 1098 - <a 1099 - href="#" 1100 - class="link" 1101 - @click.prevent="goto(event.handle)" 1102 - x-text="event.handle" 1103 - ></a> 1104 - </td> 1105 - </tr> 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> 1106 1127 </template> 1107 1128 </tbody> 1108 1129 </table> 1109 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> 1110 1137 </div> 1111 1138 1112 - </template> 1139 + <template x-if="did.startsWith('did:plc:')"> 1140 + <div x-data="pdsHistory(did, pds)"> 1141 + <h3 class="text-lg mt-3"> 1142 + PLC PDS history 1143 + </h3> 1144 + <div class="overflow-x-auto"> 1145 + <table class="table table-xs"> 1146 + <tbody> 1147 + <template x-if="loading"> 1148 + <tr> 1149 + <td>Loading&hellip;</td> 1150 + </tr> 1151 + </template> 1152 + <template x-if="error"> 1153 + <tr> 1154 + <td>Error: <span x-text="error"></span></td> 1155 + </tr> 1156 + </template> 1157 + <template x-if="!loading && !error && history.length === 0"> 1158 + <tr> 1159 + <td class="text-sm"> 1160 + <em>no previous PDS</em> 1161 + </td> 1162 + </tr> 1163 + </template> 1164 + <template x-for="event in history"> 1165 + <tr x-data="didRepoState(did, event.pds)"> 1166 + <td> 1167 + <code x-text="event.date.split('T')[0]"></code> 1168 + </td> 1169 + <td> 1170 + <a 1171 + href="#" 1172 + class="link" 1173 + @click.prevent="goto(event.pds)" 1174 + x-text="event.pds" 1175 + ></a> 1176 + </td> 1177 + <template x-if="event.current"> 1178 + <td> 1179 + <span 1180 + x-show="state && !state.active" 1181 + x-text="state && state.status" 1182 + class="badge badge-sm badge-soft badge-warning" 1183 + ></span> 1184 + <span 1185 + x-show="state && state.active" 1186 + class="badge badge-sm badge-soft badge-success" 1187 + >current</span> 1188 + </td> 1189 + </template> 1190 + <template x-if="!event.current"> 1191 + <td> 1192 + <span 1193 + x-show="state && !state.active" 1194 + x-text="state && state.status" 1195 + class="badge badge-sm badge-soft badge-success" 1196 + ></span> 1197 + <span 1198 + x-show="state && state.active" 1199 + class="badge badge-sm badge-soft badge-warning" 1200 + >active</span> 1201 + </td> 1202 + </template> 1203 + </tr> 1204 + </template> 1205 + </tbody> 1206 + </table> 1207 + </div> 1208 + </div> 1209 + </template> 1210 + </div> 1211 + </template> 1212 + </div> 1213 + </div> 1214 + </template> 1215 + 1216 + <template x-if="handle !== null"> 1217 + <div class="card bg-base-100 w-full max-w-2xl shrink-0 shadow-2xl m-4"> 1218 + <div 1219 + x-data="checkHandle(handle)" 1220 + x-init="$watch('handle', h => updateHandle(h))" 1221 + class="card-body" 1222 + > 1223 + <h2 class="card-title"> 1224 + <span class="badge badge-secondary">Handle</span> 1225 + <span x-text="handle"></span> 1226 + </h2> 1227 + 1228 + <h3 class="text-lg mt-3"> 1229 + Resolution 1230 + </h3> 1231 + <p x-show="loading" class="text-i">Loading&hellip;</p> 1232 + <div x-show="!loading" class="overflow-x-auto"> 1233 + <table class="table table-xs"> 1234 + <tbody> 1235 + <tr> 1236 + <td class="text-sm">DNS</td> 1237 + <td class="text-sm"> 1238 + <code x-text="dnsDid"></code> 1239 + </td> 1240 + <td> 1241 + <div 1242 + class="badge badge-sm badge-soft badge-neutral" 1243 + x-show="dnsErr !== null" 1244 + x-text="dnsErr" 1245 + ></div> 1246 + </td> 1247 + </tr> 1248 + <tr> 1249 + <td class="text-sm">Http</td> 1250 + <td class="text-sm"> 1251 + <code x-text="httpDid"></code> 1252 + </td> 1253 + <td> 1254 + <div 1255 + class="badge badge-sm badge-soft badge-neutral" 1256 + x-show="httpErr !== null" 1257 + x-text="httpErr" 1258 + ></div> 1259 + </td> 1260 + </tr> 1261 + </tbody> 1262 + </table> 1113 1263 </div> 1264 + 1265 + <template x-if="did !== null && did.startsWith('did:plc:')"> 1266 + 1267 + <div x-data="handleHistory(did, handle)"> 1268 + <h3 class="text-lg mt-3"> 1269 + PLC handle history 1270 + </h3> 1271 + <div class="overflow-x-auto"> 1272 + <table class="table table-xs"> 1273 + <tbody> 1274 + <template x-if="loading"> 1275 + <tr> 1276 + <td>Loading&hellip;</td> 1277 + </tr> 1278 + </template> 1279 + <template x-if="error"> 1280 + <tr> 1281 + <td>Error: <span x-text="error"></span></td> 1282 + </tr> 1283 + </template> 1284 + <template x-if="!loading && !error && history.length === 0"> 1285 + <tr> 1286 + <td class="text-sm"> 1287 + <em>no previous handle</em> 1288 + </td> 1289 + </tr> 1290 + </template> 1291 + <template x-for="event in history"> 1292 + <tr> 1293 + <td> 1294 + <code x-text="event.date.split('T')[0]"></code> 1295 + </td> 1296 + <td> 1297 + <a 1298 + href="#" 1299 + class="link" 1300 + @click.prevent="goto(event.handle)" 1301 + x-text="event.handle" 1302 + ></a> 1303 + </td> 1304 + </tr> 1305 + </template> 1306 + </tbody> 1307 + </table> 1308 + </div> 1309 + </div> 1310 + 1311 + </template> 1114 1312 </div> 1115 - </template> 1116 - </div> 1313 + </div> 1314 + </template> 1117 1315 </div> 1316 + 1317 + 1318 + 1319 + <div class="footer text-xs sm:footer-horizontal text-neutral mt-32 p-8 max-w-2xl mx-auto"> 1320 + <nav> 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> 1331 + </nav> 1332 + 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> 1339 + 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> 1349 + </nav> 1350 + </div> 1351 + 1118 1352 </body> 1119 1353 </html>
+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 |