CINXE.COM

Sync - AT Protocol

<!DOCTYPE html><html lang="en" class="h-full"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/adf4eead961bdeef.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-a44c2cc8a8388eca.js"/><script src="/_next/static/chunks/fd9d1056-5ee502662f283a0c.js" async=""></script><script src="/_next/static/chunks/7023-33e5340857d3c2a8.js" async=""></script><script src="/_next/static/chunks/main-app-33f6b09c3d9a1b9b.js" async=""></script><script src="/_next/static/chunks/231-181ea92841931ecc.js" async=""></script><script src="/_next/static/chunks/1364-c5ca11b1c706d695.js" async=""></script><script src="/_next/static/chunks/7915-4e4e4521ff2dd284.js" async=""></script><script src="/_next/static/chunks/app/%5Blocale%5D/specs/sync/page-badda9d36090f13a.js" async=""></script><script src="/_next/static/chunks/2533-375920c1c2b49e69.js" async=""></script><script src="/_next/static/chunks/902-72f88913c77b5544.js" async=""></script><script src="/_next/static/chunks/1953-6d7c09a02aa0c5ee.js" async=""></script><script src="/_next/static/chunks/app/%5Blocale%5D/layout-13692be05679dab2.js" async=""></script><title>Sync - AT Protocol</title><meta name="description" content="Firehose and other data synchronization mechanisms."/><meta property="og:title" content="Sync - AT Protocol"/><meta property="og:description" content="Firehose and other data synchronization mechanisms."/><meta property="og:url" content="https://atproto.com/"/><meta property="og:site_name" content="AT Protocol"/><meta property="og:image" content="https://atproto.com/default-social-card.png"/><meta property="og:image:secure_url" content="https://atproto.com/default-social-card.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:type" content="website"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:title" content="Sync - AT Protocol"/><meta name="twitter:description" content="Firehose and other data synchronization mechanisms."/><meta name="twitter:image" content="https://atproto.com/default-social-card.png"/><script src="/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js" noModule=""></script></head><body class="flex min-h-full bg-white antialiased dark:bg-zinc-900"><script>!function(){try{var d=document.documentElement,c=d.classList;c.remove('light','dark');var e=localStorage.getItem('theme');if('system'===e||(!e&&true)){var t='(prefers-color-scheme: dark)',m=window.matchMedia(t);if(m.media!==t||m.matches){d.style.colorScheme = 'dark';c.add('dark')}else{d.style.colorScheme = 'light';c.add('light')}}else if(e){c.add(e|| '')}if(e==='light'||e==='dark')d.style.colorScheme=e}catch(e){}}()</script><div class="w-full"><div class="h-full lg:ml-72 xl:ml-80"><header class="contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex"><div class="contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:px-6 lg:pb-8 lg:pt-4 xl:w-80 lg:dark:border-white/10"><div class="hidden lg:flex"><a aria-label="Home" href="/"><svg viewBox="0 0 162 28" aria-hidden="true" class="h-6"><path class="fill-blue-500" d="M14.362 27.78c-1.956 0-3.756-.324-5.4-.972-1.644-.648-3.072-1.566-4.284-2.754a12.247 12.247 0 0 1-2.808-4.158c-.66-1.596-.99-3.33-.99-5.202 0-2.232.342-4.212 1.026-5.94.696-1.728 1.656-3.18 2.88-4.356a12.48 12.48 0 0 1 4.284-2.7c1.632-.612 3.378-.918 5.238-.918 2.28 0 4.278.354 5.994 1.062 1.716.708 3.144 1.668 4.284 2.88a11.706 11.706 0 0 1 2.538 4.158c.552 1.548.804 3.168.756 4.86-.06 2.328-.546 4.116-1.458 5.364-.912 1.236-2.328 1.854-4.248 1.854a5.839 5.839 0 0 1-2.826-.702 3.703 3.703 0 0 1-1.764-2.07l1.044.054c-.492.924-1.164 1.572-2.016 1.944a6.464 6.464 0 0 1-2.61.558c-1.212 0-2.28-.258-3.204-.774a5.682 5.682 0 0 1-2.178-2.214c-.528-.948-.792-2.046-.792-3.294 0-1.284.276-2.394.828-3.33a5.77 5.77 0 0 1 2.232-2.196c.936-.516 1.992-.774 3.168-.774.78 0 1.59.162 2.43.486.852.324 1.512.78 1.98 1.368l-.738.936V8.664h2.412l-.054 6.462c0 .924.18 1.62.54 2.088.36.468.894.702 1.602.702.624 0 1.104-.174 1.44-.522.348-.36.588-.846.72-1.458a10.66 10.66 0 0 0 .252-2.106c.036-1.86-.24-3.426-.828-4.698-.588-1.272-1.386-2.298-2.394-3.078a9.499 9.499 0 0 0-3.294-1.71c-1.2-.36-2.394-.54-3.582-.54-1.68 0-3.174.27-4.482.81-1.308.528-2.412 1.278-3.312 2.25-.888.96-1.56 2.1-2.016 3.42-.444 1.308-.654 2.748-.63 4.32.048 1.56.33 2.964.846 4.212a9.324 9.324 0 0 0 2.16 3.204 9.38 9.38 0 0 0 3.276 2.034c1.26.468 2.64.702 4.14.702.84 0 1.674-.096 2.502-.288.84-.18 1.608-.438 2.304-.774l1.026 2.808c-.924.432-1.896.75-2.916.954a14.649 14.649 0 0 1-3.078.324Zm-.144-10.098c.852 0 1.566-.246 2.142-.738.576-.492.864-1.326.864-2.502 0-1.068-.258-1.872-.774-2.412-.504-.552-1.218-.828-2.142-.828-1.092 0-1.908.288-2.448.864-.54.576-.81 1.368-.81 2.376 0 1.032.276 1.83.828 2.394.564.564 1.344.846 2.34.846Z"></path><path class="fill-blue-600 dark:fill-blue-500" d="M51.799 7.813V5.545h13.509v2.268H59.86V23h-2.624V7.812h-5.438ZM39.392 23h-2.795l6.28-17.455h3.043L52.203 23h-2.796L44.472 8.716h-.137L39.392 23Zm.469-6.835h9.068v2.216h-9.068v-2.216Z"></path><path class="fill-zinc-700 dark:fill-zinc-400" d="M161.144 5.545V23h-2.548V5.545h2.548ZM149.649 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.131-2.361-.505-1.012-.758-2.194-.758-3.546 0-1.358.253-2.545.758-3.562.506-1.017 1.216-1.807 2.131-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.506 1.017.758 2.204.758 3.562 0 1.352-.252 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.009-2.139c.795 0 1.454-.21 1.977-.63.523-.421.909-.98 1.159-1.68.256-.699.384-1.468.384-2.31 0-.834-.128-1.602-.384-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.977-.639-.801 0-1.466.213-1.995.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.645 1.258 1.167 1.678.529.42 1.194.631 1.995.631ZM136.032 23.264c-1.267 0-2.358-.287-3.273-.86-.909-.58-1.608-1.378-2.096-2.395-.489-1.018-.733-2.182-.733-3.495 0-1.33.25-2.503.75-3.52.5-1.022 1.204-1.82 2.113-2.395.909-.573 1.98-.86 3.213-.86.995 0 1.881.184 2.659.554a4.764 4.764 0 0 1 1.884 1.534c.483.659.77 1.429.861 2.31h-2.48a2.974 2.974 0 0 0-.938-1.586c-.483-.443-1.13-.665-1.943-.665-.71 0-1.332.188-1.866.563-.529.37-.941.898-1.236 1.585-.296.682-.443 1.489-.443 2.42 0 .955.144 1.779.434 2.472.29.693.699 1.23 1.227 1.61.535.382 1.162.572 1.884.572.483 0 .92-.088 1.313-.264.397-.182.73-.44.997-.776.272-.335.463-.739.571-1.21h2.48a4.823 4.823 0 0 1-.827 2.267 4.76 4.76 0 0 1-1.849 1.568c-.767.38-1.668.571-2.702.571ZM121.571 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.131-2.361-.505-1.012-.758-2.194-.758-3.546 0-1.358.253-2.545.758-3.562.506-1.017 1.216-1.807 2.131-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.505 1.017.758 2.204.758 3.562 0 1.352-.253 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.009-2.139c.795 0 1.454-.21 1.977-.63.523-.421.909-.98 1.159-1.68.256-.699.383-1.468.383-2.31 0-.834-.127-1.602-.383-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.977-.639-.802 0-1.466.213-1.995.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.645 1.258 1.167 1.678.529.42 1.193.631 1.995.631ZM113.379 9.91v2.044h-7.151V9.91h7.151Zm-5.233-3.137h2.548v12.383c0 .495.074.867.221 1.117.148.244.339.412.572.503.238.085.497.127.775.127.205 0 .384-.014.537-.042l.358-.068.46 2.105a4.307 4.307 0 0 1-.63.17 4.992 4.992 0 0 1-1.023.102 4.483 4.483 0 0 1-1.875-.358 3.208 3.208 0 0 1-1.406-1.159c-.358-.522-.537-1.179-.537-1.968V6.773ZM98.321 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.13-2.361-.506-1.012-.76-2.194-.76-3.546 0-1.358.254-2.545.76-3.562.505-1.017 1.215-1.807 2.13-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.505 1.017.758 2.204.758 3.562 0 1.352-.253 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.008-2.139c.796 0 1.455-.21 1.978-.63.523-.421.909-.98 1.159-1.68.256-.699.383-1.468.383-2.31 0-.834-.127-1.602-.383-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.978-.639-.8 0-1.465.213-1.994.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.644 1.258 1.167 1.678.529.42 1.193.631 1.995.631ZM84.065 23V9.91h2.463v2.079h.136a3.164 3.164 0 0 1 1.261-1.662 3.61 3.61 0 0 1 2.063-.614 10.896 10.896 0 0 1 1.082.06v2.437a4.577 4.577 0 0 0-.545-.094 5.202 5.202 0 0 0-.784-.06c-.603 0-1.14.129-1.611.384a2.85 2.85 0 0 0-1.517 2.566V23h-2.548ZM68.918 23V5.545h6.221c1.358 0 2.483.248 3.375.742.892.494 1.56 1.17 2.003 2.028.443.853.665 1.813.665 2.881 0 1.074-.225 2.04-.674 2.898-.443.852-1.113 1.528-2.01 2.028-.893.494-2.015.742-3.367.742h-4.279V14.63h4.04c.858 0 1.554-.148 2.088-.444.534-.3.926-.71 1.176-1.227.25-.517.375-1.105.375-1.764 0-.66-.125-1.244-.375-1.756-.25-.511-.645-.912-1.184-1.201-.534-.29-1.239-.435-2.114-.435h-3.307V23h-2.633Z"></path></svg></a></div><div class="fixed inset-x-0 top-0 z-50 flex h-14 items-center justify-between gap-12 px-4 transition sm:px-6 lg:left-72 lg:z-30 lg:px-8 xl:left-80 backdrop-blur-sm lg:left-72 xl:left-80 dark:backdrop-blur bg-white/[var(--bg-opacity-light)] dark:bg-zinc-900/[var(--bg-opacity-dark)]" style="--bg-opacity-light:0.5;--bg-opacity-dark:0.2"><div class="absolute inset-x-0 top-full h-px transition bg-zinc-900/7.5 dark:bg-white/7.5"></div><div class="hidden lg:block lg:max-w-md lg:flex-auto"><button type="button" class="hidden h-8 w-full items-center gap-2 rounded-full bg-white pl-2 pr-3 text-sm text-zinc-500 ring-1 ring-zinc-900/10 transition hover:ring-zinc-900/20 ui-not-focus-visible:outline-none lg:flex dark:bg-white/5 dark:text-zinc-400 dark:ring-inset dark:ring-white/10 dark:hover:ring-white/20"><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-5 w-5 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" d="M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25"></path></svg>Find something...<kbd class="ml-auto text-2xs text-zinc-400 dark:text-zinc-500"><kbd class="font-sans"></kbd><kbd class="font-sans">K</kbd></kbd></button><!--$--><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span><!--/$--></div><div class="flex items-center gap-5 lg:hidden"><button type="button" class="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5" aria-label="Toggle navigation"><svg viewBox="0 0 10 9" fill="none" stroke-linecap="round" aria-hidden="true" class="w-2.5 stroke-zinc-900 dark:stroke-white"><path d="M.5 1h9M.5 8h9M.5 4.5h9"></path></svg></button><!--$--><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span><!--/$--><a aria-label="Home" href="/"><svg viewBox="0 0 162 28" aria-hidden="true" class="h-6"><path class="fill-blue-500" d="M14.362 27.78c-1.956 0-3.756-.324-5.4-.972-1.644-.648-3.072-1.566-4.284-2.754a12.247 12.247 0 0 1-2.808-4.158c-.66-1.596-.99-3.33-.99-5.202 0-2.232.342-4.212 1.026-5.94.696-1.728 1.656-3.18 2.88-4.356a12.48 12.48 0 0 1 4.284-2.7c1.632-.612 3.378-.918 5.238-.918 2.28 0 4.278.354 5.994 1.062 1.716.708 3.144 1.668 4.284 2.88a11.706 11.706 0 0 1 2.538 4.158c.552 1.548.804 3.168.756 4.86-.06 2.328-.546 4.116-1.458 5.364-.912 1.236-2.328 1.854-4.248 1.854a5.839 5.839 0 0 1-2.826-.702 3.703 3.703 0 0 1-1.764-2.07l1.044.054c-.492.924-1.164 1.572-2.016 1.944a6.464 6.464 0 0 1-2.61.558c-1.212 0-2.28-.258-3.204-.774a5.682 5.682 0 0 1-2.178-2.214c-.528-.948-.792-2.046-.792-3.294 0-1.284.276-2.394.828-3.33a5.77 5.77 0 0 1 2.232-2.196c.936-.516 1.992-.774 3.168-.774.78 0 1.59.162 2.43.486.852.324 1.512.78 1.98 1.368l-.738.936V8.664h2.412l-.054 6.462c0 .924.18 1.62.54 2.088.36.468.894.702 1.602.702.624 0 1.104-.174 1.44-.522.348-.36.588-.846.72-1.458a10.66 10.66 0 0 0 .252-2.106c.036-1.86-.24-3.426-.828-4.698-.588-1.272-1.386-2.298-2.394-3.078a9.499 9.499 0 0 0-3.294-1.71c-1.2-.36-2.394-.54-3.582-.54-1.68 0-3.174.27-4.482.81-1.308.528-2.412 1.278-3.312 2.25-.888.96-1.56 2.1-2.016 3.42-.444 1.308-.654 2.748-.63 4.32.048 1.56.33 2.964.846 4.212a9.324 9.324 0 0 0 2.16 3.204 9.38 9.38 0 0 0 3.276 2.034c1.26.468 2.64.702 4.14.702.84 0 1.674-.096 2.502-.288.84-.18 1.608-.438 2.304-.774l1.026 2.808c-.924.432-1.896.75-2.916.954a14.649 14.649 0 0 1-3.078.324Zm-.144-10.098c.852 0 1.566-.246 2.142-.738.576-.492.864-1.326.864-2.502 0-1.068-.258-1.872-.774-2.412-.504-.552-1.218-.828-2.142-.828-1.092 0-1.908.288-2.448.864-.54.576-.81 1.368-.81 2.376 0 1.032.276 1.83.828 2.394.564.564 1.344.846 2.34.846Z"></path><path class="fill-blue-600 dark:fill-blue-500" d="M51.799 7.813V5.545h13.509v2.268H59.86V23h-2.624V7.812h-5.438ZM39.392 23h-2.795l6.28-17.455h3.043L52.203 23h-2.796L44.472 8.716h-.137L39.392 23Zm.469-6.835h9.068v2.216h-9.068v-2.216Z"></path><path class="fill-zinc-700 dark:fill-zinc-400" d="M161.144 5.545V23h-2.548V5.545h2.548ZM149.649 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.131-2.361-.505-1.012-.758-2.194-.758-3.546 0-1.358.253-2.545.758-3.562.506-1.017 1.216-1.807 2.131-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.506 1.017.758 2.204.758 3.562 0 1.352-.252 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.009-2.139c.795 0 1.454-.21 1.977-.63.523-.421.909-.98 1.159-1.68.256-.699.384-1.468.384-2.31 0-.834-.128-1.602-.384-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.977-.639-.801 0-1.466.213-1.995.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.645 1.258 1.167 1.678.529.42 1.194.631 1.995.631ZM136.032 23.264c-1.267 0-2.358-.287-3.273-.86-.909-.58-1.608-1.378-2.096-2.395-.489-1.018-.733-2.182-.733-3.495 0-1.33.25-2.503.75-3.52.5-1.022 1.204-1.82 2.113-2.395.909-.573 1.98-.86 3.213-.86.995 0 1.881.184 2.659.554a4.764 4.764 0 0 1 1.884 1.534c.483.659.77 1.429.861 2.31h-2.48a2.974 2.974 0 0 0-.938-1.586c-.483-.443-1.13-.665-1.943-.665-.71 0-1.332.188-1.866.563-.529.37-.941.898-1.236 1.585-.296.682-.443 1.489-.443 2.42 0 .955.144 1.779.434 2.472.29.693.699 1.23 1.227 1.61.535.382 1.162.572 1.884.572.483 0 .92-.088 1.313-.264.397-.182.73-.44.997-.776.272-.335.463-.739.571-1.21h2.48a4.823 4.823 0 0 1-.827 2.267 4.76 4.76 0 0 1-1.849 1.568c-.767.38-1.668.571-2.702.571ZM121.571 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.131-2.361-.505-1.012-.758-2.194-.758-3.546 0-1.358.253-2.545.758-3.562.506-1.017 1.216-1.807 2.131-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.505 1.017.758 2.204.758 3.562 0 1.352-.253 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.009-2.139c.795 0 1.454-.21 1.977-.63.523-.421.909-.98 1.159-1.68.256-.699.383-1.468.383-2.31 0-.834-.127-1.602-.383-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.977-.639-.802 0-1.466.213-1.995.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.645 1.258 1.167 1.678.529.42 1.193.631 1.995.631ZM113.379 9.91v2.044h-7.151V9.91h7.151Zm-5.233-3.137h2.548v12.383c0 .495.074.867.221 1.117.148.244.339.412.572.503.238.085.497.127.775.127.205 0 .384-.014.537-.042l.358-.068.46 2.105a4.307 4.307 0 0 1-.63.17 4.992 4.992 0 0 1-1.023.102 4.483 4.483 0 0 1-1.875-.358 3.208 3.208 0 0 1-1.406-1.159c-.358-.522-.537-1.179-.537-1.968V6.773ZM98.321 23.264c-1.227 0-2.298-.281-3.213-.843-.915-.563-1.625-1.35-2.13-2.361-.506-1.012-.76-2.194-.76-3.546 0-1.358.254-2.545.76-3.562.505-1.017 1.215-1.807 2.13-2.37.915-.562 1.986-.843 3.213-.843s2.298.28 3.213.843 1.625 1.353 2.131 2.37c.505 1.017.758 2.204.758 3.562 0 1.352-.253 2.534-.758 3.546-.506 1.011-1.216 1.798-2.131 2.36-.915.563-1.986.844-3.213.844Zm.008-2.139c.796 0 1.455-.21 1.978-.63.523-.421.909-.98 1.159-1.68.256-.699.383-1.468.383-2.31 0-.834-.127-1.602-.383-2.3-.25-.705-.636-1.27-1.159-1.697-.523-.426-1.182-.639-1.978-.639-.8 0-1.465.213-1.994.64-.522.426-.912.991-1.167 1.695a6.789 6.789 0 0 0-.375 2.302c0 .84.125 1.61.375 2.31.255.698.644 1.258 1.167 1.678.529.42 1.193.631 1.995.631ZM84.065 23V9.91h2.463v2.079h.136a3.164 3.164 0 0 1 1.261-1.662 3.61 3.61 0 0 1 2.063-.614 10.896 10.896 0 0 1 1.082.06v2.437a4.577 4.577 0 0 0-.545-.094 5.202 5.202 0 0 0-.784-.06c-.603 0-1.14.129-1.611.384a2.85 2.85 0 0 0-1.517 2.566V23h-2.548ZM68.918 23V5.545h6.221c1.358 0 2.483.248 3.375.742.892.494 1.56 1.17 2.003 2.028.443.853.665 1.813.665 2.881 0 1.074-.225 2.04-.674 2.898-.443.852-1.113 1.528-2.01 2.028-.893.494-2.015.742-3.367.742h-4.279V14.63h4.04c.858 0 1.554-.148 2.088-.444.534-.3.926-.71 1.176-1.227.25-.517.375-1.105.375-1.764 0-.66-.125-1.244-.375-1.756-.25-.511-.645-.912-1.184-1.201-.534-.29-1.239-.435-2.114-.435h-3.307V23h-2.633Z"></path></svg></a></div><div class="flex items-center gap-5"><nav class="hidden md:block"><ul role="list" class="flex items-center gap-8"><li><a class="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/sdks">SDKs</a></li><li><a class="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="https://docs.bsky.app/blog">Blog</a></li><li><a class="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="https://github.com/bluesky-social/atproto">GitHub</a></li><select class="block w-full appearance-none rounded-md border-0 py-1.5 pl-3 pr-3 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6 dark:bg-gray-800 dark:text-gray-100 dark:ring-gray-700"><option value="en" selected="">English</option><option value="pt">Português</option><option value="ja">日本語</option><option value="ko">한국어</option></select></ul></nav><div class="hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-white/15"></div><div class="flex gap-4"><div class="contents lg:hidden"><button type="button" class="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 ui-not-focus-visible:outline-none lg:hidden dark:hover:bg-white/5" aria-label="Find something..."><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-5 w-5 stroke-zinc-900 dark:stroke-white"><path stroke-linecap="round" stroke-linejoin="round" d="M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25"></path></svg></button><!--$--><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span><!--/$--></div><button type="button" class="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5" aria-label="Toggle theme"><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-5 w-5 stroke-zinc-900 dark:hidden"><path d="M12.5 10a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0Z"></path><path stroke-linecap="round" d="M10 5.5v-1M13.182 6.818l.707-.707M14.5 10h1M13.182 13.182l.707.707M10 15.5v-1M6.11 13.889l.708-.707M4.5 10h1M6.11 6.111l.708.707"></path></svg><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="hidden h-5 w-5 stroke-white dark:block"><path d="M15.224 11.724a5.5 5.5 0 0 1-6.949-6.949 5.5 5.5 0 1 0 6.949 6.949Z"></path></svg></button></div></div></div><nav class="hidden lg:mt-10 lg:block"><ul role="list"><li class="md:hidden"><a class="block py-1 text-sm text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/">API</a></li><li class="md:hidden"><a class="block py-1 text-sm text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="#">Documentation</a></li><li class="md:hidden"><a class="block py-1 text-sm text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="#">Support</a></li><li class="relative mt-6 md:mt-0"><h2 class="text-xs font-semibold text-zinc-900 dark:text-white">Home</h2><div class="relative mt-3 pl-2"><div class="absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5"></div><ul role="list" class="border-l border-transparent"><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/"><span class="truncate">Introduction</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/sdks"><span class="truncate">SDKs</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/glossary"><span class="truncate">Glossary</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/faq"><span class="truncate">FAQ</span></a></li></ul></div></li><li class="relative mt-6"><h2 class="text-xs font-semibold text-zinc-900 dark:text-white">Building apps</h2><div class="relative mt-3 pl-2"><div class="absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5"></div><ul role="list" class="border-l border-transparent"><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/applications"><span class="truncate">Quick start</span></a></li></ul></div></li><li class="relative mt-6"><h2 class="text-xs font-semibold text-zinc-900 dark:text-white">Guides</h2><div class="relative mt-3 pl-2"><div class="absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5"></div><ul role="list" class="border-l border-transparent"><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/overview"><span class="truncate">Overview</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/identity"><span class="truncate">Identity</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/data-repos"><span class="truncate">Data Repositories</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/lexicon"><span class="truncate">Schemas &amp; Lexicon</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/guides/self-hosting"><span class="truncate">PDS Self-Hosting</span></a></li></ul></div></li><li class="relative mt-6"><h2 class="text-xs font-semibold text-zinc-900 dark:text-white">Specs</h2><div class="relative mt-3 pl-2"><div class="absolute inset-x-0 top-0 bg-zinc-800/2.5 will-change-transform dark:bg-white/2.5" style="border-radius:8px;height:32px;top:352px;opacity:0"></div><div class="absolute inset-y-0 left-2 w-px bg-zinc-900/10 dark:bg-white/5"></div><div class="absolute left-2 h-6 w-px bg-blue-500" style="top:356px;opacity:1"></div><ul role="list" class="border-l border-transparent"><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/atp"><span class="truncate">AT Protocol</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/data-model"><span class="truncate">Data Model</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/lexicon"><span class="truncate">Lexicon</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/cryptography"><span class="truncate">Cryptography</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/account"><span class="truncate">Accounts</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/repository"><span class="truncate">Repository</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/blob"><span class="truncate">Blobs</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/label"><span class="truncate">Labels</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/xrpc"><span class="truncate">HTTP API (XRPC)</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/oauth"><span class="truncate">OAuth</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/event-stream"><span class="truncate">Event Stream</span></a></li><li class="relative"><a aria-current="page" class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-900 dark:text-white" href="/specs/sync"><span class="truncate">Sync</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/did"><span class="truncate">DID</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/handle"><span class="truncate">Handle</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/nsid"><span class="truncate">NSID</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/tid"><span class="truncate">TID</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/record-key"><span class="truncate">Record Key</span></a></li><li class="relative"><a class="flex justify-between gap-2 py-1 pr-3 text-sm transition pl-4 text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white" href="/specs/at-uri-scheme"><span class="truncate">URI Scheme</span></a></li></ul></div></li></ul></nav></div></header><div class="relative flex h-full flex-col px-4 pt-14 sm:px-6 lg:px-8"><main class="flex-auto"><article class="flex h-full flex-col pb-24 pt-16"><div class="flex-auto prose dark:prose-invert [html_:where(&amp;&gt;*)]:mx-auto [html_:where(&amp;&gt;*)]:max-w-2xl [html_:where(&amp;&gt;*)]:lg:mx-[calc(50%-min(50%,theme(maxWidth.lg)))] [html_:where(&amp;&gt;*)]:lg:max-w-3xl"><h1>Data Synchronization</h1> <p>One of the main design goals of atproto (the &quot;Authenticated Transfer Protocol&quot;) is to reliably distribute public content between independent network services. This data transfer should be trustworthy (cryptographicly authenticated) and relatively low-latency even at large scale. It is also important that new participants can join the network at any time and “backfill” prior content.</p> <p>This section describes the major data synchronization features in atproto. The primary real-time data synchronization mechanism is repository event streams, commonly referred to as &quot;firehoses&quot;. The primary batch data transfer mechanism is repository exports as CAR files. These two mechanisms can be combined in a &quot;bootstrapping&quot; process which result in a live-synchronized copy of the network.</p> <h2 class="scroll-mt-24" id="synchronization-primitives"><a class="group text-inherit no-underline hover:text-inherit" href="#synchronization-primitives">Synchronization Primitives</a></h2> <p>As described in the repository spec, each commit to a repository has a <em>revision</em> number, in TID syntax. The revision number must always increase between commits for the same account, even if the account is migrated between hosts or has a period of inactivity in the network. Revision numbers can be used as a logical clock to aid synchronization of individual accounts. To keep this simple, it is recommended to use the current time as a TID for each commit, including the initial commit when creating a new account. Services should reject or ignore revision numbers corresponding to future timestamps (beyond a short fuzzy time drift window). Network services can track the commit revision for every account they have seen, and use this to verify synchronization progress. Services which synchronize data can include the most-recently-processed revision in HTTP responses to API requests from the account in question, in the <code>Atproto-Repo-Rev</code> HTTP response header. This allows clients (and users) to detect if the response is up-to-date with the actual repository, and detect any problems with synchronization.</p> <h2 class="scroll-mt-24" id="firehose"><a class="group text-inherit no-underline hover:text-inherit" href="#firehose">Firehose</a></h2> <p>The repository event stream (<code>com.atproto.sync.subscribeRepos</code>, also called the &quot;firehose&quot;) is an <a href="/specs/event-stream">Event Stream</a> which broadcasts updates to repositories (<code>#commit</code> events), handles and DID documents (<code>#identity</code>), and account hosting status (<code>#account</code>). PDS hosts provide a single stream with updates for all locally-hosted accounts. &quot;Relays&quot; are network services which subscribe to one or more repo streams (eg, multiple PDS instances) and aggregate them in to a single combined repo stream. The combined stream has the same structure and event types. A Relay which aggregates nearly all accounts from nearly all PDS instances in the network (possibly through intermediate relays) outputs a “full-network” firehose. Relays often mirror and can re-distribute the repository contents, though their core functionality is to verify content and output a unified firehose.</p> <p>In most cases the repository data synchronized over the firehose is self-certifying (contains verifiable signatures), and consumers can verify content without making additional requests directly to account PDS instances. It is possible for services to redact events from the firehose, such that downstream services would not be aware of new content.</p> <p>Identity and account information is <em>not</em> self-certifying, and services may need need to verify independently. This usually means independent DID and <a href="/specs/handle">handle resolution</a>. Account hosting status might also be checked at account PDS hosts, to disambiguate hosting status at different pieces of infrastructure.</p> <p>The event message types are declared in the <code>com.atproto.sync.subscribeRepos</code> Lexicon schema, and are summarized below. A few fields are the same for all event types (except for <code>repo</code> vs <code>did</code> for <code>#commit</code> events):</p> <ul> <li><code>seq</code> (integer, required): used to ensure reliable consumption, as described in Event Streams</li> <li><code>did</code> / <code>repo</code>(string with DID syntax, required): the account/identity associated with the event</li> <li><code>time</code> (string with datetime syntax, required): an informal and non-authoritative estimate of when event was received. Intermediate services may decide to pass this field through as-is, or update to the current time</li> </ul> <h3><code>#identity</code> Events</h3> <p>Indicates that there <em>may</em> have been a change to the indicated identity (meaning the DID document or handle), and optionally what the current handle is. Does not indicate what changed, or reliably indicate what the current state of the identity is.</p> <p>Event fields:</p> <ul> <li><code>seq</code> (integer, required): same for all event types</li> <li><code>did</code> (string with DID syntax, required): same for all event types</li> <li><code>time</code> (string with datetime syntax, required): same for all event types</li> <li><code>handle</code> (string with handle syntax, optional): the current handle for this identity. May be <code>handle.invalid</code> if the handle does not currently resolve correctly.</li> </ul> <p>Presence or absence of the <code>handle</code> field does not indicate that it is the handle which has changed.</p> <p>The semantics and expected behavior are that downstream services should update any cached identity metadata (including DID document and handle) for the indicated DID. They might mark caches as stale, immediately purge cached data, or attempt to re-resolve metadata.</p> <p>Identity events are emitted on a &quot;best-effort&quot; basis. It is possible for the DID document or handle resolution status to change without any atproto service detecting the change, in which case an event would not be emitted. It is also possible for the event to be emitted redundantly, when nothing has actually changed.</p> <p>Intermediate services (eg, relays) may chose to modify or pass through identity events:</p> <ul> <li>they may replace the handle with the result of their own resolution; or always remove the handle field; or always pass it through unaltered</li> <li>they may filter out identity events if they observe that identity has not actually changed</li> <li>they may emit identity events based on changes they became aware of independently (eg, via periodic re-validation of handles)</li> </ul> <h3><code>#account</code> Events</h3> <p>Indicates that there may have been a change in <a href="/specs/account">Account Hosting status</a> at the service which emits the event, and what the new status is. For example, it could be the result of creation, deletion, or temporary suspension of an account. The event describes the current hosting status, not what changed.</p> <p>Event Fields:</p> <ul> <li><code>seq</code> (integer, required): same for all event types</li> <li><code>did</code> (string with DID syntax, required): same for all event types</li> <li><code>time</code> (string with datetime syntax, required): same for all event types</li> <li><code>active</code> (boolean, required): whether the repository is currently available and can be redistributed</li> <li><code>status</code> (string, optional): string status code which describes the account state in more detail. Known values include:<!-- --> <ul> <li><code>takendown</code>: indefinite removal of the repository by a service provider, due to a terms or policy violation</li> <li><code>suspended</code>: temporary or time-limited variant of <code>takedown</code></li> <li><code>deleted</code>: account has been deactivated, possibly permanently.</li> <li><code>deactivated</code>: temporary or indefinite removal of all public data by the account themselves.</li> </ul> </li> </ul> <p>When coming from any service which redistributes account data, the event describes what the new status is <em>at that service</em>, and is authoritative in that context. In other words, the event is hop-by-hop for repository hosts and mirrors.</p> <p>See the Account Hosting specification for more details.</p> <h3><code>#commit</code> Events</h3> <p>This event indicates that there has been a new repository commit for the indicated account. The event usually contains the &quot;diff&quot; of repository data, in the form of a CAR slice. See the <a href="/specs/repository">Repository specification</a> for details on &quot;diffs&quot; and the CAR file format.</p> <p>See the Repository specification for more details around repo diffs.</p> <p>Event Fields:</p> <ul> <li><code>seq</code> (integer, required): same for all event types</li> <li><code>repo</code> (string with DID syntax, required): the same as <code>did</code> for all other event types</li> <li><code>time</code> (string with datetime syntax, required): same for all event types</li> <li><code>rev</code> (string with TID syntax, required): the revision of the commit. Must match the <code>rev</code> in the commit block itself.</li> <li><code>since</code> (string with TID syntax, nullable): indicates the <code>rev</code> of a preceding commit, which the the repo diff contains differences from</li> <li><code>commit</code> (cid-link, required): CID of the commit object (in <code>blocks</code>)</li> <li><code>tooBig</code> (boolean, required): if true, indicates that the repo diff was too large, and that <code>blocks</code>, <code>ops</code>, and complete <code>blobs</code> are not all included</li> <li><code>blocks</code> (bytes, required): CAR &quot;slice&quot; for the corresponding repo diff. The commit object must always be included.</li> <li><code>ops</code> (array of objects, required): list of record-level operations in this commit: specific records created, updated, deleted</li> <li><code>blobs</code> (array of cid-link, required): set of new blobs (by CID) referenced by records in this commit</li> </ul> <p>Commit events are broadcast when the account repository changes. Commits can be &quot;empty&quot;, meaning no actual record content changed, and only the <code>rev</code> was incremented. They can contain a single record update, or multiple updates. Only the commit object, record blocks, and MST tree nodes are authenticated (signed): the <code>since</code>, <code>ops</code>, <code>blobs</code>, and <code>tooBig</code> fields are not self-certifying, and could in theory be manipulated, or otherwise be incorrect or incomplete.</p> <p>If <code>since</code> is not included, the commit should include the full repo tree, or set the <code>tooBig</code> flag.</p> <p>If the <code>tooBig</code> flag is set, then the amount of updated data was too much to be serialized in a single stream event message. Downstream services which want to maintain complete synchronized copies for the repo need to fetch the diff separately, as discussed below.</p> <h3>Firehose Validation Best Practices</h3> <p>A service which does full validation of upstream events has a number of properties to track and check. For example, Relay instances should fully validate content from PDS instances before re-broadcasting.</p> <p>Here is a summary of validation rules and behaviors:</p> <ul> <li>services should independently resolve identity data for each DID. They should ignore <code>#commit</code> events for accounts which do not have a functioning atproto identity (eg, lacking a signing key, or lacking a PDS service entry, or for which the DID has been tombstoned)</li> <li>services which subscribe directly to PDS instances should keep track of which PDS is authoritative for each DID. They should remember the host each subscription (WebSocket) is connected to, and reject <code>#commit</code> events for accounts if they come from a stream which does not correspond to the current account for that DID</li> <li>services should track account hosting status for each DID, and ignore <code>#commit</code> events for events which are not <code>active</code></li> <li>services should verify commit signatures for each <code>#commit</code> event, using the current identity data. If the signature initially fails to verify, the service should refresh the identity metadata in case it had recently changed. Events with conclusively invalid signatures should be rejected.</li> <li>services should reject any event messages which exceed reasonable limits. A reasonable upper bound for producers is 5 MBytes (for any event stream message type). The <code>subscribeRepos</code> Lexicon also limits <code>blocks</code> to one million bytes, and <code>ops</code> to 200 entries. Commits with too much data must use the <code>tooBig</code> mechanism, though such commits should generally be avoided in the first place by breaking them up in to multiple smaller commits.</li> <li>services should verify that repository data structures are valid against the specification. Missing fields, incorrect MST structure, or other protocol-layer violations should result in events being rejected.</li> <li>services may apply rate-limits to identity, account, and commit events, and suspend accounts or upstream services which violate those limits. Rate limits might also be applied to recovery modes such as invalid signatures resulting in an identity refresh, <code>tooBig</code> events, missing or out-of-order commits, etc.</li> <li>services should ignore commit events with a <code>rev</code> lower or equal to the most recent processed <code>rev</code> for that DID, and should reject commit events with a <code>rev</code> corresponding to a future timestamp (beyond a clock drift window of a few minutes)</li> <li>services should check the <code>since</code> value in commit events, and if it is not consistent with the previous seen <code>rev</code> for that DID (see discussion in &quot;Reliable Synchronization&quot;), mark the repo as out-of-sync (similar to a <code>tooBig</code> commit event)</li> <li>data limits on records specifically should be verified. Events containing corrupt or entirely invalid records may be rejected. for example, a record not being CBOR at all, or exceeding normal data size limits.</li> <li>more subtle data validation of records may be enforced, or may be ignored, depending on the service. For example, unsupported CID hash types embedded in records should probably be ignored by Relays (even if they violate the atproto data model), but may result in the record or commit event being rejected by an AppView</li> <li>mirroring services, which retain a full copy of repository data, should verify that commit diffs leave the MST tree in a complete and valid state (eg, no missing records, no invalid MST nodes, commit CID would be reproducible if the MST structure was re-generated from scratch)</li> <li>Relays (specifically) should not validate records against Lexicons</li> </ul> <h2 class="scroll-mt-24" id="reliable-synchronization"><a class="group text-inherit no-underline hover:text-inherit" href="#reliable-synchronization">Reliable Synchronization</a></h2> <p>This section describes some details on how to reliably subscribe to the firehose and maintain an existing synchronized mirror of the network.</p> <p>Services should generally maintain a few pieces of state for all accounts they are tracking data from:</p> <ul> <li>track the most recent commit <code>rev</code> they have successfully processed</li> <li>keep cached identity data, and use cache expiration to ensure periodic re-validation of that data</li> <li>track account status</li> </ul> <p>Identity caches should be purged any time an <code>#identity</code> event is received. Additionally, identity resolution should be refreshed if a commit signature fails to verify, in case the signing key was updated but the identity cache has not been updated yet.</p> <p>When <code>tooBig</code> events are emitted on the firehose, downstream services will need to fetch the diff out-of-band. This usually means an API request to the <code>com.atproto.sync.getRepo</code> endpoint on the current PDS host for the account, with the <code>since</code> field included. The <code>since</code> value should be the most recently processed <code>rev</code> value for the account, which may or may not match the <code>since</code> field in the commit event message.</p> <p>If a <code>#commit</code> is received with a <code>since</code> that does not match the most recently processed <code>rev</code> for the account, and is “later” (higher value) than the most recent commit <code>rev</code> the service has processed for that account, the service may need to do the same kind of out-of-band fetch as for a <code>tooBig</code> event.</p> <p>Services should keep track of the <code>seq</code> number of their upstream subscriptions. This should be stored separately per-upstream, even if there is only a single Relay connection, in case a different Relay is subscribed to in the future (which will have different <code>seq</code> numbers).</p> <p>Events can be processed concurrently, but they should be processed sequentially in-order for any given account. This can be accomplished by partitioning multiple workers using the repo DID as a partitioning key.</p> <p>Services can confirm that they are consuming content reliably by fetching a snapshot of repository DIDs and <code>rev</code> numbers from other services, including PDS hosts and Relay instances. After a short delay, these can be compared against the current state of the service to identify any accounts which have lower than expected <code>rev</code> numbers. These repos can then be updated out-of-band.</p> <h2 class="scroll-mt-24" id="bootstrapping-a-live-mirror"><a class="group text-inherit no-underline hover:text-inherit" href="#bootstrapping-a-live-mirror">Bootstrapping a Live Mirror</a></h2> <p>The firehose can be used to follow new data updates, and repo exports can be used for snapshots. Actually combining the two to bootstrap a complete live-updating mirror can be a bit tricky. One approach is described below.</p> <p>Keep a sync status table for all accounts (DIDs) encountered. The status can be:</p> <ul> <li><code>dirty</code>: there is either no local repo data for this account, or it has gotten out of sync</li> <li><code>in-process</code>: the repo is &quot;dirty&quot;, but there is a background task in process to update it</li> <li><code>synchronized</code>: a complete copy of the repository has been processed</li> </ul> <p>Start by subscribing to the full firehose. If there is no existing repository data for the account, mark the account as &quot;dirty&quot;. When new events come in for a repo, the behavior depends on the repo state. If it is &quot;dirty&quot;, the event is ignored. If the state is &quot;synchronized&quot;, the event is immediately processed as an update to the repo. If the state is &quot;in-process&quot;, the event is enqueued locally.</p> <p>Have a set of background workers start processing &quot;dirty&quot; repos. First they mark the status as <code>in-process</code>, so that new events are enqueued locally. Then the full repo export (CAR file) is fetched from the PDS and processed in full. The commit <code>rev</code> of the repo export is noted. When the full repo import is complete, the worker can start processing any enqueued events, in order, skipping any with a <code>rev</code> lower than the existing repo processed <code>rev</code> (as is the usual behavior). When the queue for the account is fully processed, the state can be flipped to <code>synchronized</code>, and the worker can move on.</p> <p>After some time, most of the known accounts will be marked as <code>synchronized</code>, though this will only represent the most recently active accounts in the network. Next a more complete set of repositories in the network can be fetched, for example using an API query against an existing large service. Any new identified accounts can be marked as <code>dirty</code> in the service, and the background workers can start processing them.</p> <p>When all of the accounts are <code>synchronized</code>, the process is complete. At large scale it may be hard to get perfect synchronization: PDS instances may be down at various times, identities may fail to resolve, or invalid events, data, or signatures may end up in the network.</p> <h2 class="scroll-mt-24" id="usage-and-implementation-guidelines"><a class="group text-inherit no-underline hover:text-inherit" href="#usage-and-implementation-guidelines">Usage and Implementation Guidelines</a></h2> <p>Guidelines for specific firehose event sequencing during different account events are described in an <a href="/guides/account-lifecycle">Account Lifecycle Best Practices guide</a>.</p> <h2 class="scroll-mt-24" id="security-concerns"><a class="group text-inherit no-underline hover:text-inherit" href="#security-concerns">Security Concerns</a></h2> <p>General mitigations for resource exhaustion attacks are recommended: event rate-limits, data quotas per account, limits on data object sizes and deserialized data complexity, etc.</p> <p>Care should always be taken when making network requests to unknown or untrusted hosts, especially when the network locators for those host from from untrusted input. This includes validating URLs to not connect to local or internal hosts (including via HTTP redirects), avoiding SSRF in browser contexts, etc.</p> <p>To prevent traffic amplification attacks, outbound network requests should be rate-limited by host. For example, identity resolution requests when consuming from the firehose, including DNS TXT traffic volume and DID resolution requests.</p> <h2 class="scroll-mt-24" id="future-work"><a class="group text-inherit no-underline hover:text-inherit" href="#future-work">Future Work</a></h2> <p>The <code>subscribeRepos</code> Lexicon is likely to be tweaked, with deprecated fields removed, even if this breaks Lexicon evolution rules.</p> <p>The event stream sequence/cursor scheme may be iterated on to support sharding, timestamp-based resumption, and easier failover between independent instances.</p> <p>Alternatives to the full authenticated firehose may be added to the protocol. For example, simple JSON serialization, filtering by record collection type, omitting MST nodes, and other changes which would simplify development and reduce resource consumption for use-cases where full authentication is not necessary or desired.</p></div></article></main><footer class="mx-auto w-full max-w-2xl space-y-10 pb-16 lg:max-w-5xl"><div class="flex"><div class="flex flex-col items-start gap-3"><a class="inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300" aria-label="Previous: Event Stream" href="/specs/event-stream"><svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="mt-0.5 h-5 w-5 -ml-1 rotate-180"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"></path></svg>Previous</a><a tabindex="-1" aria-hidden="true" class="text-base font-semibold text-zinc-900 transition hover:text-zinc-600 dark:text-white dark:hover:text-zinc-300" href="/specs/event-stream">Event Stream</a></div><div class="ml-auto flex flex-col items-end gap-3"><a class="inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300" aria-label="Next: DID" href="/specs/did">Next<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="mt-0.5 h-5 w-5 -mr-1"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"></path></svg></a><a tabindex="-1" aria-hidden="true" class="text-base font-semibold text-zinc-900 transition hover:text-zinc-600 dark:text-white dark:hover:text-zinc-300" href="/specs/did">DID</a></div></div><div class="flex flex-col items-center justify-between gap-5 border-t border-zinc-900/5 pt-8 sm:flex-row dark:border-white/5"><p class="text-xs text-zinc-600 dark:text-zinc-400">© Copyright <!-- -->2025<!-- -->. All rights reserved.</p><div class="flex gap-4"><a class="group" href="https://bsky.app/profile/atproto.com"><span class="sr-only">Follow us on Bluesky</span><svg viewBox="0 0 360 320" aria-hidden="true" class="h-5 w-5 fill-zinc-700 transition group-hover:fill-zinc-900 dark:group-hover:fill-zinc-500"><path d="M180 141.964C163.699 110.262 119.308 51.1817 78.0347 22.044C38.4971 -5.86834 23.414 -1.03207 13.526 3.43594C2.08093 8.60755 0 26.1785 0 36.5164C0 46.8542 5.66748 121.272 9.36416 133.694C21.5786 174.738 65.0603 188.607 105.104 184.156C107.151 183.852 109.227 183.572 111.329 183.312C109.267 183.642 107.19 183.924 105.104 184.156C46.4204 192.847 -5.69621 214.233 62.6582 290.33C137.848 368.18 165.705 273.637 180 225.702C194.295 273.637 210.76 364.771 295.995 290.33C360 225.702 313.58 192.85 254.896 184.158C252.81 183.926 250.733 183.645 248.671 183.315C250.773 183.574 252.849 183.855 254.896 184.158C294.94 188.61 338.421 174.74 350.636 133.697C354.333 121.275 360 46.8568 360 36.519C360 26.1811 357.919 8.61012 346.474 3.43851C336.586 -1.02949 321.503 -5.86576 281.965 22.0466C240.692 51.1843 196.301 110.262 180 141.964Z"></path></svg></a><a class="group" href="https://github.com/bluesky-social"><span class="sr-only">Follow us on GitHub</span><svg viewBox="0 0 20 20" aria-hidden="true" class="h-5 w-5 fill-zinc-700 transition group-hover:fill-zinc-900 dark:group-hover:fill-zinc-500"><path fill-rule="evenodd" clip-rule="evenodd" d="M10 1.667c-4.605 0-8.334 3.823-8.334 8.544 0 3.78 2.385 6.974 5.698 8.106.417.075.573-.182.573-.406 0-.203-.011-.875-.011-1.592-2.093.397-2.635-.522-2.802-1.002-.094-.246-.5-1.005-.854-1.207-.291-.16-.708-.556-.01-.567.656-.01 1.124.62 1.281.876.75 1.292 1.948.93 2.427.705.073-.555.291-.93.531-1.143-1.854-.213-3.791-.95-3.791-4.218 0-.929.322-1.698.854-2.296-.083-.214-.375-1.09.083-2.265 0 0 .698-.224 2.292.876a7.576 7.576 0 0 1 2.083-.288c.709 0 1.417.096 2.084.288 1.593-1.11 2.291-.875 2.291-.875.459 1.174.167 2.05.084 2.263.53.599.854 1.357.854 2.297 0 3.278-1.948 4.005-3.802 4.219.302.266.563.78.563 1.58 0 1.143-.011 2.061-.011 2.35 0 .224.156.491.573.405a8.365 8.365 0 0 0 4.11-3.116 8.707 8.707 0 0 0 1.567-4.99c0-4.721-3.73-8.545-8.334-8.545Z"></path></svg></a></div></div></footer></div></div></div><script src="/_next/static/chunks/webpack-a44c2cc8a8388eca.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/adf4eead961bdeef.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[5751,[],\"\"]\n5:I[9275,[],\"\"]\n7:I[1343,[],\"\"]\na:I[6130,[],\"\"]\n6:[\"locale\",\"en\",\"d\"]\nb:[]\n"])</script><script>self.__next_f.push([1,"0:[null,[\"$\",\"$L2\",null,{\"buildId\":\"JaN0T6ybhl3_vUFMuuIKY\",\"assetPrefix\":\"\",\"initialCanonicalUrl\":\"/specs/sync\",\"initialTree\":[\"\",{\"children\":[[\"locale\",\"en\",\"d\"],{\"children\":[\"specs\",{\"children\":[\"sync\",{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",true]}],\"initialSeedData\":[\"\",{\"children\":[[\"locale\",\"en\",\"d\"],{\"children\":[\"specs\",{\"children\":[\"sync\",{\"children\":[\"__PAGE__\",{},[[\"$L3\",\"$L4\"],null],null]},[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"$6\",\"children\",\"specs\",\"children\",\"sync\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\",\"styles\":null}],null]},[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"$6\",\"children\",\"specs\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\",\"styles\":null}],null]},[\"$L8\",null],null]},[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[],\"styles\":[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/adf4eead961bdeef.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]]}],null],\"couldBeIntercepted\":false,\"initialHead\":[null,\"$L9\"],\"globalErrorComponent\":\"$a\",\"missingSlots\":\"$Wb\"}]]\n"])</script><script>self.__next_f.push([1,"c:I[2734,[\"231\",\"static/chunks/231-181ea92841931ecc.js\",\"1364\",\"static/chunks/1364-c5ca11b1c706d695.js\",\"7915\",\"static/chunks/7915-4e4e4521ff2dd284.js\",\"6120\",\"static/chunks/app/%5Blocale%5D/specs/sync/page-badda9d36090f13a.js\"],\"Heading\"]\nd:I[5294,[\"231\",\"static/chunks/231-181ea92841931ecc.js\",\"1364\",\"static/chunks/1364-c5ca11b1c706d695.js\",\"7915\",\"static/chunks/7915-4e4e4521ff2dd284.js\",\"6120\",\"static/chunks/app/%5Blocale%5D/specs/sync/page-badda9d36090f13a.js\"],\"Code\"]\ne:I[231,[\"231\",\"static/chunks/231-181ea92841931ecc.js\",\"1364\",\"static/chunks/1364-c5ca11b1c706d695.js\",\"7915\",\"static/chunks/7915-4e4e4521ff2dd284.js\",\"6120\",\"static/chunks/app/%5Blocale%5D/specs/sync/page-badda9d36090f13a.js\"],\"\"]\n"])</script><script>self.__next_f.push([1,"4:[\"$\",\"article\",null,{\"className\":\"flex h-full flex-col pb-24 pt-16\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex-auto prose dark:prose-invert [html_:where(\u0026\u003e*)]:mx-auto [html_:where(\u0026\u003e*)]:max-w-2xl [html_:where(\u0026\u003e*)]:lg:mx-[calc(50%-min(50%,theme(maxWidth.lg)))] [html_:where(\u0026\u003e*)]:lg:max-w-3xl\",\"children\":[[\"$\",\"h1\",null,{\"children\":\"Data Synchronization\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"One of the main design goals of atproto (the \\\"Authenticated Transfer Protocol\\\") is to reliably distribute public content between independent network services. This data transfer should be trustworthy (cryptographicly authenticated) and relatively low-latency even at large scale. It is also important that new participants can join the network at any time and “backfill” prior content.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"This section describes the major data synchronization features in atproto. The primary real-time data synchronization mechanism is repository event streams, commonly referred to as \\\"firehoses\\\". The primary batch data transfer mechanism is repository exports as CAR files. These two mechanisms can be combined in a \\\"bootstrapping\\\" process which result in a live-synchronized copy of the network.\"}],\"\\n\",[\"$\",\"$Lc\",null,{\"level\":2,\"id\":\"synchronization-primitives\",\"children\":\"Synchronization Primitives\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"As described in the repository spec, each commit to a repository has a \",[\"$\",\"em\",null,{\"children\":\"revision\"}],\" number, in TID syntax. The revision number must always increase between commits for the same account, even if the account is migrated between hosts or has a period of inactivity in the network. Revision numbers can be used as a logical clock to aid synchronization of individual accounts. To keep this simple, it is recommended to use the current time as a TID for each commit, including the initial commit when creating a new account. Services should reject or ignore revision numbers corresponding to future timestamps (beyond a short fuzzy time drift window). Network services can track the commit revision for every account they have seen, and use this to verify synchronization progress. Services which synchronize data can include the most-recently-processed revision in HTTP responses to API requests from the account in question, in the \",[\"$\",\"$Ld\",null,{\"children\":\"Atproto-Repo-Rev\"}],\" HTTP response header. This allows clients (and users) to detect if the response is up-to-date with the actual repository, and detect any problems with synchronization.\"]}],\"\\n\",[\"$\",\"$Lc\",null,{\"level\":2,\"id\":\"firehose\",\"children\":\"Firehose\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The repository event stream (\",[\"$\",\"$Ld\",null,{\"children\":\"com.atproto.sync.subscribeRepos\"}],\", also called the \\\"firehose\\\") is an \",[\"$\",\"$Le\",null,{\"href\":\"/specs/event-stream\",\"children\":\"Event Stream\"}],\" which broadcasts updates to repositories (\",[\"$\",\"$Ld\",null,{\"children\":\"#commit\"}],\" events), handles and DID documents (\",[\"$\",\"$Ld\",null,{\"children\":\"#identity\"}],\"), and account hosting status (\",[\"$\",\"$Ld\",null,{\"children\":\"#account\"}],\"). PDS hosts provide a single stream with updates for all locally-hosted accounts. \\\"Relays\\\" are network services which subscribe to one or more repo streams (eg, multiple PDS instances) and aggregate them in to a single combined repo stream. The combined stream has the same structure and event types. A Relay which aggregates nearly all accounts from nearly all PDS instances in the network (possibly through intermediate relays) outputs a “full-network” firehose. Relays often mirror and can re-distribute the repository contents, though their core functionality is to verify content and output a unified firehose.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"In most cases the repository data synchronized over the firehose is self-certifying (contains verifiable signatures), and consumers can verify content without making additional requests directly to account PDS instances. It is possible for services to redact events from the firehose, such that downstream services would not be aware of new content.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Identity and account information is \",[\"$\",\"em\",null,{\"children\":\"not\"}],\" self-certifying, and services may need need to verify independently. This usually means independent DID and \",[\"$\",\"$Le\",null,{\"href\":\"/specs/handle\",\"children\":\"handle resolution\"}],\". Account hosting status might also be checked at account PDS hosts, to disambiguate hosting status at different pieces of infrastructure.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The event message types are declared in the \",[\"$\",\"$Ld\",null,{\"children\":\"com.atproto.sync.subscribeRepos\"}],\" Lexicon schema, and are summarized below. A few fields are the same for all event types (except for \",[\"$\",\"$Ld\",null,{\"children\":\"repo\"}],\" vs \",[\"$\",\"$Ld\",null,{\"children\":\"did\"}],\" for \",[\"$\",\"$Ld\",null,{\"children\":\"#commit\"}],\" events):\"]}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"seq\"}],\" (integer, required): used to ensure reliable consumption, as described in Event Streams\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"did\"}],\" / \",[\"$\",\"$Ld\",null,{\"children\":\"repo\"}],\"(string with DID syntax, required): the account/identity associated with the event\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"time\"}],\" (string with datetime syntax, required): an informal and non-authoritative estimate of when event was received. Intermediate services may decide to pass this field through as-is, or update to the current time\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"#identity\"}],\" Events\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Indicates that there \",[\"$\",\"em\",null,{\"children\":\"may\"}],\" have been a change to the indicated identity (meaning the DID document or handle), and optionally what the current handle is. Does not indicate what changed, or reliably indicate what the current state of the identity is.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Event fields:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"seq\"}],\" (integer, required): same for all event types\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"did\"}],\" (string with DID syntax, required): same for all event types\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"time\"}],\" (string with datetime syntax, required): same for all event types\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"handle\"}],\" (string with handle syntax, optional): the current handle for this identity. May be \",[\"$\",\"$Ld\",null,{\"children\":\"handle.invalid\"}],\" if the handle does not currently resolve correctly.\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Presence or absence of the \",[\"$\",\"$Ld\",null,{\"children\":\"handle\"}],\" field does not indicate that it is the handle which has changed.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The semantics and expected behavior are that downstream services should update any cached identity metadata (including DID document and handle) for the indicated DID. They might mark caches as stale, immediately purge cached data, or attempt to re-resolve metadata.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Identity events are emitted on a \\\"best-effort\\\" basis. It is possible for the DID document or handle resolution status to change without any atproto service detecting the change, in which case an event would not be emitted. It is also possible for the event to be emitted redundantly, when nothing has actually changed.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Intermediate services (eg, relays) may chose to modify or pass through identity events:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":\"they may replace the handle with the result of their own resolution; or always remove the handle field; or always pass it through unaltered\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"they may filter out identity events if they observe that identity has not actually changed\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"they may emit identity events based on changes they became aware of independently (eg, via periodic re-validation of handles)\"}],\"\\n\"]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"#account\"}],\" Events\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Indicates that there may have been a change in \",[\"$\",\"$Le\",null,{\"href\":\"/specs/account\",\"children\":\"Account Hosting status\"}],\" at the service which emits the event, and what the new status is. For example, it could be the result of creation, deletion, or temporary suspension of an account. The event describes the current hosting status, not what changed.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Event Fields:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"seq\"}],\" (integer, required): same for all event types\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"did\"}],\" (string with DID syntax, required): same for all event types\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"time\"}],\" (string with datetime syntax, required): same for all event types\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"active\"}],\" (boolean, required): whether the repository is currently available and can be redistributed\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"status\"}],\" (string, optional): string status code which describes the account state in more detail. Known values include:\",\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"takendown\"}],\": indefinite removal of the repository by a service provider, due to a terms or policy violation\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"suspended\"}],\": temporary or time-limited variant of \",[\"$\",\"$Ld\",null,{\"children\":\"takedown\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"deleted\"}],\": account has been deactivated, possibly permanently.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"deactivated\"}],\": temporary or indefinite removal of all public data by the account themselves.\"]}],\"\\n\"]}],\"\\n\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"When coming from any service which redistributes account data, the event describes what the new status is \",[\"$\",\"em\",null,{\"children\":\"at that service\"}],\", and is authoritative in that context. In other words, the event is hop-by-hop for repository hosts and mirrors.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"See the Account Hosting specification for more details.\"}],\"\\n\",[\"$\",\"h3\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"#commit\"}],\" Events\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"This event indicates that there has been a new repository commit for the indicated account. The event usually contains the \\\"diff\\\" of repository data, in the form of a CAR slice. See the \",[\"$\",\"$Le\",null,{\"href\":\"/specs/repository\",\"children\":\"Repository specification\"}],\" for details on \\\"diffs\\\" and the CAR file format.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"See the Repository specification for more details around repo diffs.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Event Fields:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"seq\"}],\" (integer, required): same for all event types\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"repo\"}],\" (string with DID syntax, required): the same as \",[\"$\",\"$Ld\",null,{\"children\":\"did\"}],\" for all other event types\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"time\"}],\" (string with datetime syntax, required): same for all event types\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" (string with TID syntax, required): the revision of the commit. Must match the \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" in the commit block itself.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"since\"}],\" (string with TID syntax, nullable): indicates the \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" of a preceding commit, which the the repo diff contains differences from\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"commit\"}],\" (cid-link, required): CID of the commit object (in \",[\"$\",\"$Ld\",null,{\"children\":\"blocks\"}],\")\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"tooBig\"}],\" (boolean, required): if true, indicates that the repo diff was too large, and that \",[\"$\",\"$Ld\",null,{\"children\":\"blocks\"}],\", \",[\"$\",\"$Ld\",null,{\"children\":\"ops\"}],\", and complete \",[\"$\",\"$Ld\",null,{\"children\":\"blobs\"}],\" are not all included\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"blocks\"}],\" (bytes, required): CAR \\\"slice\\\" for the corresponding repo diff. The commit object must always be included.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"ops\"}],\" (array of objects, required): list of record-level operations in this commit: specific records created, updated, deleted\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"blobs\"}],\" (array of cid-link, required): set of new blobs (by CID) referenced by records in this commit\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Commit events are broadcast when the account repository changes. Commits can be \\\"empty\\\", meaning no actual record content changed, and only the \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" was incremented. They can contain a single record update, or multiple updates. Only the commit object, record blocks, and MST tree nodes are authenticated (signed): the \",[\"$\",\"$Ld\",null,{\"children\":\"since\"}],\", \",[\"$\",\"$Ld\",null,{\"children\":\"ops\"}],\", \",[\"$\",\"$Ld\",null,{\"children\":\"blobs\"}],\", and \",[\"$\",\"$Ld\",null,{\"children\":\"tooBig\"}],\" fields are not self-certifying, and could in theory be manipulated, or otherwise be incorrect or incomplete.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"If \",[\"$\",\"$Ld\",null,{\"children\":\"since\"}],\" is not included, the commit should include the full repo tree, or set the \",[\"$\",\"$Ld\",null,{\"children\":\"tooBig\"}],\" flag.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"If the \",[\"$\",\"$Ld\",null,{\"children\":\"tooBig\"}],\" flag is set, then the amount of updated data was too much to be serialized in a single stream event message. Downstream services which want to maintain complete synchronized copies for the repo need to fetch the diff separately, as discussed below.\"]}],\"\\n\",[\"$\",\"h3\",null,{\"children\":\"Firehose Validation Best Practices\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"A service which does full validation of upstream events has a number of properties to track and check. For example, Relay instances should fully validate content from PDS instances before re-broadcasting.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Here is a summary of validation rules and behaviors:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[\"services should independently resolve identity data for each DID. They should ignore \",[\"$\",\"$Ld\",null,{\"children\":\"#commit\"}],\" events for accounts which do not have a functioning atproto identity (eg, lacking a signing key, or lacking a PDS service entry, or for which the DID has been tombstoned)\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"services which subscribe directly to PDS instances should keep track of which PDS is authoritative for each DID. They should remember the host each subscription (WebSocket) is connected to, and reject \",[\"$\",\"$Ld\",null,{\"children\":\"#commit\"}],\" events for accounts if they come from a stream which does not correspond to the current account for that DID\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"services should track account hosting status for each DID, and ignore \",[\"$\",\"$Ld\",null,{\"children\":\"#commit\"}],\" events for events which are not \",[\"$\",\"$Ld\",null,{\"children\":\"active\"}]]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"services should verify commit signatures for each \",[\"$\",\"$Ld\",null,{\"children\":\"#commit\"}],\" event, using the current identity data. If the signature initially fails to verify, the service should refresh the identity metadata in case it had recently changed. Events with conclusively invalid signatures should be rejected.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"services should reject any event messages which exceed reasonable limits. A reasonable upper bound for producers is 5 MBytes (for any event stream message type). The \",[\"$\",\"$Ld\",null,{\"children\":\"subscribeRepos\"}],\" Lexicon also limits \",[\"$\",\"$Ld\",null,{\"children\":\"blocks\"}],\" to one million bytes, and \",[\"$\",\"$Ld\",null,{\"children\":\"ops\"}],\" to 200 entries. Commits with too much data must use the \",[\"$\",\"$Ld\",null,{\"children\":\"tooBig\"}],\" mechanism, though such commits should generally be avoided in the first place by breaking them up in to multiple smaller commits.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"services should verify that repository data structures are valid against the specification. Missing fields, incorrect MST structure, or other protocol-layer violations should result in events being rejected.\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"services may apply rate-limits to identity, account, and commit events, and suspend accounts or upstream services which violate those limits. Rate limits might also be applied to recovery modes such as invalid signatures resulting in an identity refresh, \",[\"$\",\"$Ld\",null,{\"children\":\"tooBig\"}],\" events, missing or out-of-order commits, etc.\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"services should ignore commit events with a \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" lower or equal to the most recent processed \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" for that DID, and should reject commit events with a \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" corresponding to a future timestamp (beyond a clock drift window of a few minutes)\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[\"services should check the \",[\"$\",\"$Ld\",null,{\"children\":\"since\"}],\" value in commit events, and if it is not consistent with the previous seen \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" for that DID (see discussion in \\\"Reliable Synchronization\\\"), mark the repo as out-of-sync (similar to a \",[\"$\",\"$Ld\",null,{\"children\":\"tooBig\"}],\" commit event)\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"data limits on records specifically should be verified. Events containing corrupt or entirely invalid records may be rejected. for example, a record not being CBOR at all, or exceeding normal data size limits.\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"more subtle data validation of records may be enforced, or may be ignored, depending on the service. For example, unsupported CID hash types embedded in records should probably be ignored by Relays (even if they violate the atproto data model), but may result in the record or commit event being rejected by an AppView\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"mirroring services, which retain a full copy of repository data, should verify that commit diffs leave the MST tree in a complete and valid state (eg, no missing records, no invalid MST nodes, commit CID would be reproducible if the MST structure was re-generated from scratch)\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"Relays (specifically) should not validate records against Lexicons\"}],\"\\n\"]}],\"\\n\",[\"$\",\"$Lc\",null,{\"level\":2,\"id\":\"reliable-synchronization\",\"children\":\"Reliable Synchronization\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"This section describes some details on how to reliably subscribe to the firehose and maintain an existing synchronized mirror of the network.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Services should generally maintain a few pieces of state for all accounts they are tracking data from:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[\"track the most recent commit \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" they have successfully processed\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"keep cached identity data, and use cache expiration to ensure periodic re-validation of that data\"}],\"\\n\",[\"$\",\"li\",null,{\"children\":\"track account status\"}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Identity caches should be purged any time an \",[\"$\",\"$Ld\",null,{\"children\":\"#identity\"}],\" event is received. Additionally, identity resolution should be refreshed if a commit signature fails to verify, in case the signing key was updated but the identity cache has not been updated yet.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"When \",[\"$\",\"$Ld\",null,{\"children\":\"tooBig\"}],\" events are emitted on the firehose, downstream services will need to fetch the diff out-of-band. This usually means an API request to the \",[\"$\",\"$Ld\",null,{\"children\":\"com.atproto.sync.getRepo\"}],\" endpoint on the current PDS host for the account, with the \",[\"$\",\"$Ld\",null,{\"children\":\"since\"}],\" field included. The \",[\"$\",\"$Ld\",null,{\"children\":\"since\"}],\" value should be the most recently processed \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" value for the account, which may or may not match the \",[\"$\",\"$Ld\",null,{\"children\":\"since\"}],\" field in the commit event message.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"If a \",[\"$\",\"$Ld\",null,{\"children\":\"#commit\"}],\" is received with a \",[\"$\",\"$Ld\",null,{\"children\":\"since\"}],\" that does not match the most recently processed \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" for the account, and is “later” (higher value) than the most recent commit \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" the service has processed for that account, the service may need to do the same kind of out-of-band fetch as for a \",[\"$\",\"$Ld\",null,{\"children\":\"tooBig\"}],\" event.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Services should keep track of the \",[\"$\",\"$Ld\",null,{\"children\":\"seq\"}],\" number of their upstream subscriptions. This should be stored separately per-upstream, even if there is only a single Relay connection, in case a different Relay is subscribed to in the future (which will have different \",[\"$\",\"$Ld\",null,{\"children\":\"seq\"}],\" numbers).\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Events can be processed concurrently, but they should be processed sequentially in-order for any given account. This can be accomplished by partitioning multiple workers using the repo DID as a partitioning key.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Services can confirm that they are consuming content reliably by fetching a snapshot of repository DIDs and \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" numbers from other services, including PDS hosts and Relay instances. After a short delay, these can be compared against the current state of the service to identify any accounts which have lower than expected \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" numbers. These repos can then be updated out-of-band.\"]}],\"\\n\",[\"$\",\"$Lc\",null,{\"level\":2,\"id\":\"bootstrapping-a-live-mirror\",\"children\":\"Bootstrapping a Live Mirror\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The firehose can be used to follow new data updates, and repo exports can be used for snapshots. Actually combining the two to bootstrap a complete live-updating mirror can be a bit tricky. One approach is described below.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Keep a sync status table for all accounts (DIDs) encountered. The status can be:\"}],\"\\n\",[\"$\",\"ul\",null,{\"children\":[\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"dirty\"}],\": there is either no local repo data for this account, or it has gotten out of sync\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"in-process\"}],\": the repo is \\\"dirty\\\", but there is a background task in process to update it\"]}],\"\\n\",[\"$\",\"li\",null,{\"children\":[[\"$\",\"$Ld\",null,{\"children\":\"synchronized\"}],\": a complete copy of the repository has been processed\"]}],\"\\n\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Start by subscribing to the full firehose. If there is no existing repository data for the account, mark the account as \\\"dirty\\\". When new events come in for a repo, the behavior depends on the repo state. If it is \\\"dirty\\\", the event is ignored. If the state is \\\"synchronized\\\", the event is immediately processed as an update to the repo. If the state is \\\"in-process\\\", the event is enqueued locally.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Have a set of background workers start processing \\\"dirty\\\" repos. First they mark the status as \",[\"$\",\"$Ld\",null,{\"children\":\"in-process\"}],\", so that new events are enqueued locally. Then the full repo export (CAR file) is fetched from the PDS and processed in full. The commit \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" of the repo export is noted. When the full repo import is complete, the worker can start processing any enqueued events, in order, skipping any with a \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" lower than the existing repo processed \",[\"$\",\"$Ld\",null,{\"children\":\"rev\"}],\" (as is the usual behavior). When the queue for the account is fully processed, the state can be flipped to \",[\"$\",\"$Ld\",null,{\"children\":\"synchronized\"}],\", and the worker can move on.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"After some time, most of the known accounts will be marked as \",[\"$\",\"$Ld\",null,{\"children\":\"synchronized\"}],\", though this will only represent the most recently active accounts in the network. Next a more complete set of repositories in the network can be fetched, for example using an API query against an existing large service. Any new identified accounts can be marked as \",[\"$\",\"$Ld\",null,{\"children\":\"dirty\"}],\" in the service, and the background workers can start processing them.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"When all of the accounts are \",[\"$\",\"$Ld\",null,{\"children\":\"synchronized\"}],\", the process is complete. At large scale it may be hard to get perfect synchronization: PDS instances may be down at various times, identities may fail to resolve, or invalid events, data, or signatures may end up in the network.\"]}],\"\\n\",[\"$\",\"$Lc\",null,{\"level\":2,\"id\":\"usage-and-implementation-guidelines\",\"children\":\"Usage and Implementation Guidelines\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Guidelines for specific firehose event sequencing during different account events are described in an \",[\"$\",\"$Le\",null,{\"href\":\"/guides/account-lifecycle\",\"children\":\"Account Lifecycle Best Practices guide\"}],\".\"]}],\"\\n\",[\"$\",\"$Lc\",null,{\"level\":2,\"id\":\"security-concerns\",\"children\":\"Security Concerns\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"General mitigations for resource exhaustion attacks are recommended: event rate-limits, data quotas per account, limits on data object sizes and deserialized data complexity, etc.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Care should always be taken when making network requests to unknown or untrusted hosts, especially when the network locators for those host from from untrusted input. This includes validating URLs to not connect to local or internal hosts (including via HTTP redirects), avoiding SSRF in browser contexts, etc.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"To prevent traffic amplification attacks, outbound network requests should be rate-limited by host. For example, identity resolution requests when consuming from the firehose, including DNS TXT traffic volume and DID resolution requests.\"}],\"\\n\",[\"$\",\"$Lc\",null,{\"level\":2,\"id\":\"future-work\",\"children\":\"Future Work\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The \",[\"$\",\"$Ld\",null,{\"children\":\"subscribeRepos\"}],\" Lexicon is likely to be tweaked, with deprecated fields removed, even if this breaks Lexicon evolution rules.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The event stream sequence/cursor scheme may be iterated on to support sharding, timestamp-based resumption, and easier failover between independent instances.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"Alternatives to the full authenticated firehose may be added to the protocol. For example, simple JSON serialization, filtering by record collection type, omitting MST nodes, and other changes which would simplify development and reduce resource consumption for use-cases where full authentication is not necessary or desired.\"}]]}]}]\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Sync - AT Protocol\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Firehose and other data synchronization mechanisms.\"}],[\"$\",\"meta\",\"4\",{\"property\":\"og:title\",\"content\":\"Sync - AT Protocol\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:description\",\"content\":\"Firehose and other data synchronization mechanisms.\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:url\",\"content\":\"https://atproto.com/\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:site_name\",\"content\":\"AT Protocol\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:image\",\"content\":\"https://atproto.com/default-social-card.png\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:image:secure_url\",\"content\":\"https://atproto.com/default-social-card.png\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"12\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"13\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"14\",{\"name\":\"twitter:title\",\"content\":\"Sync - AT Protocol\"}],[\"$\",\"meta\",\"15\",{\"name\":\"twitter:description\",\"content\":\"Firehose and other data synchronization mechanisms.\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:image\",\"content\":\"https://atproto.com/default-social-card.png\"}]]\n3:null\n"])</script><script>self.__next_f.push([1,"f:I[5148,[\"231\",\"static/chunks/231-181ea92841931ecc.js\",\"1364\",\"static/chunks/1364-c5ca11b1c706d695.js\",\"2533\",\"static/chunks/2533-375920c1c2b49e69.js\",\"902\",\"static/chunks/902-72f88913c77b5544.js\",\"7915\",\"static/chunks/7915-4e4e4521ff2dd284.js\",\"1953\",\"static/chunks/1953-6d7c09a02aa0c5ee.js\",\"1203\",\"static/chunks/app/%5Blocale%5D/layout-13692be05679dab2.js\"],\"Providers\"]\n10:I[2808,[\"231\",\"static/chunks/231-181ea92841931ecc.js\",\"1364\",\"static/chunks/1364-c5ca11b1c706d695.js\",\"2533\",\"static/chunks/2533-375920c1c2b49e69.js\",\"902\",\"static/chunks/902-72f88913c77b5544.js\",\"7915\",\"static/chunks/7915-4e4e4521ff2dd284.js\",\"1953\",\"static/chunks/1953-6d7c09a02aa0c5ee.js\",\"1203\",\"static/chunks/app/%5Blocale%5D/layout-13692be05679dab2.js\"],\"Layout\"]\n"])</script><script>self.__next_f.push([1,"8:[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"h-full\",\"suppressHydrationWarning\":true,\"children\":[\"$\",\"body\",null,{\"className\":\"flex min-h-full bg-white antialiased dark:bg-zinc-900\",\"children\":[\"$\",\"$Lf\",null,{\"children\":[\"$\",\"div\",null,{\"className\":\"w-full\",\"children\":[\"$\",\"$L10\",null,{\"allSections\":{\"/en.mdx\":[],\"/ja.mdx\":[],\"/ko.mdx\":[],\"/pt.mdx\":[],\"/explorer\":[],\"/sdks\":[{\"title\":\"Official libraries\",\"id\":\"official-libraries\"},{\"title\":\"Community libraries\",\"id\":\"community-libraries\"}],\"/articles/atproto-for-distsys-engineers/en.mdx\":[{\"title\":\"Scaling the traditional Web backend\",\"id\":\"scaling-the-traditional-web-backend\"},{\"title\":\"Decentralizing our high-scale backend\",\"id\":\"decentralizing-our-high-scale-backend\"},{\"title\":\"Unifying the data model\",\"id\":\"unifying-the-data-model\"},{\"title\":\"Charting the flow of data\",\"id\":\"charting-the-flow-of-data\"},{\"title\":\"Building practical open systems\",\"id\":\"building-practical-open-systems\"}],\"/articles/atproto-for-distsys-engineers/ja.mdx\":[{\"title\":\"従来の Web バックエンドのスケーリング\",\"id\":\"web\"},{\"title\":\"大規模バックエンドの分散化\",\"id\":\"\"},{\"title\":\"データ モデルの統合\",\"id\":\"\"},{\"title\":\"データの流れを図に表す\",\"id\":\"\"},{\"title\":\"実用的なオープン システムの構築\",\"id\":\"\"}],\"/articles/atproto-for-distsys-engineers/ko.mdx\":[{\"title\":\"전통적인 웹 백엔드의 확장\",\"id\":\"\"},{\"title\":\"고확장 백엔드의 탈중앙화\",\"id\":\"\"},{\"title\":\"데이터 모델 통합\",\"id\":\"\"},{\"title\":\"데이터 흐름 도식화\",\"id\":\"\"},{\"title\":\"실용적인 오픈 시스템 구축\",\"id\":\"\"}],\"/articles/atproto-for-distsys-engineers/pt.mdx\":[{\"title\":\"Escalando o backend tradicional da Web\",\"id\":\"escalando-o-backend-tradicional-da-web\"},{\"title\":\"Descentralizando nosso backend de alta escala\",\"id\":\"descentralizando-nosso-backend-de-alta-escala\"},{\"title\":\"Unificando o modelo de dados\",\"id\":\"unificando-o-modelo-de-dados\"},{\"title\":\"Mapeando o fluxo de dados\",\"id\":\"mapeando-o-fluxo-de-dados\"},{\"title\":\"Construindo sistemas abertos práticos\",\"id\":\"construindo-sistemas-abertos-praticos\"}],\"/articles/why-atproto\":[],\"/guides/account-lifecycle/en.mdx\":[],\"/guides/account-lifecycle/ko.mdx\":[],\"/guides/account-migration/en.mdx\":[{\"title\":\"Creating New Account\",\"id\":\"creating-new-account\"},{\"title\":\"Migrating Data\",\"id\":\"migrating-data\"},{\"title\":\"Updating Identity\",\"id\":\"updating-identity\"},{\"title\":\"Finalizing Account Status\",\"id\":\"finalizing-account-status\"}],\"/guides/account-migration/ko.mdx\":[{\"title\":\"새로운 계정 생성\",\"id\":\"\"},{\"title\":\"데이터 마이그레이션\",\"id\":\"\"},{\"title\":\"아이덴티티 업데이트\",\"id\":\"\"},{\"title\":\"계정 상태 최종화\",\"id\":\"\"}],\"/guides/applications/en.mdx\":[{\"title\":\"Introduction\",\"id\":\"introduction\"},{\"title\":\"Step 1. Starting with our ExpressJS app\",\"id\":\"step-1-starting-with-our-express-js-app\"},{\"title\":\"Step 2. Signing in with OAuth\",\"id\":\"step-2-signing-in-with-o-auth\"},{\"title\":\"Step 3. Fetching the user's profile\",\"id\":\"step-3-fetching-the-users-profile\"},{\"title\":\"Step 4. Reading \u0026 writing records\",\"id\":\"step-4-reading-and-writing-records\"},{\"title\":\"Step 5. Creating a custom \\\"status\\\" schema\",\"id\":\"step-5-creating-a-custom-status-schema\"},{\"title\":\"Step 6. Listening to the firehose\",\"id\":\"step-6-listening-to-the-firehose\"},{\"title\":\"Step 7. Listing the latest statuses\",\"id\":\"step-7-listing-the-latest-statuses\"},{\"title\":\"Step 8. Optimistic updates\",\"id\":\"step-8-optimistic-updates\"},{\"title\":\"Thinking in AT Proto\",\"id\":\"thinking-in-at-proto\"},{\"title\":\"Next steps\",\"id\":\"next-steps\"}],\"/guides/applications/ja.mdx\":[{\"title\":\"はじめに\",\"id\":\"\"},{\"title\":\"ステップ 1. ExpressJS アプリから始める\",\"id\":\"1-express-js\"},{\"title\":\"ステップ 2. OAuth でサインイン\",\"id\":\"2-o-auth\"},{\"title\":\"ステップ 3. ユーザーのプロファイルを取得する\",\"id\":\"3\"},{\"title\":\"ステップ 4. レコードの読み取りと書き込み\",\"id\":\"4\"},{\"title\":\"ステップ 5. カスタムの「ステータス」スキーマの作成\",\"id\":\"5\"},{\"title\":\"ステップ 6. ファイアホースをリッスン\",\"id\":\"6\"},{\"title\":\"ステップ 7. 最新のステータスを一覧表示する\",\"id\":\"7\"},{\"title\":\"ステップ 8. 楽観的更新\",\"id\":\"8\"},{\"title\":\"AT Proto で考える\",\"id\":\"at-proto\"},{\"title\":\"次のステップ\",\"id\":\"\"}],\"/guides/applications/ko.mdx\":[{\"title\":\"1단계. ExpressJS 앱 시작하기\",\"id\":\"1-express-js\"},{\"title\":\"2단계. OAuth를 통한 로그인\",\"id\":\"2-o-auth\"},{\"title\":\"3단계. 사용자 프로필 가져오기\",\"id\":\"3\"},{\"title\":\"4단계. 레코드 읽기 및 쓰기\",\"id\":\"4\"},{\"title\":\"5단계. 커스텀 \\\"상태\\\" 스키마 생성\",\"id\":\"5\"},{\"title\":\"6단계. 이벤트 스트림(firehose) 구독\",\"id\":\"6-firehose\"},{\"title\":\"7단계. 최신 상태 목록 표시\",\"id\":\"7\"},{\"title\":\"8단계. 낙관적 업데이트 (Optimistic Updates)\",\"id\":\"8-optimistic-updates\"},{\"title\":\"AT Protocol 사고방식\",\"id\":\"at-protocol\"},{\"title\":\"다음 단계\",\"id\":\"\"}],\"/guides/applications/pt.mdx\":[{\"title\":\"Introdução\",\"id\":\"introducao\"},{\"title\":\"Etapa 1. Começando com nosso aplicativo ExpressJS\",\"id\":\"etapa-1-comecando-com-nosso-aplicativo-express-js\"},{\"title\":\"Etapa 2. Entrando com OAuth\",\"id\":\"etapa-2-entrando-com-o-auth\"},{\"title\":\"Etapa 3. Obtendo o perfil do usuário\",\"id\":\"etapa-3-obtendo-o-perfil-do-usuario\"},{\"title\":\"Etapa 4. Lendo e escrevendo registros\",\"id\":\"etapa-4-lendo-e-escrevendo-registros\"},{\"title\":\"Etapa 5. Criando um esquema de \\\"status\\\" personalizado\",\"id\":\"etapa-5-criando-um-esquema-de-status-personalizado\"},{\"title\":\"Etapa 6. Ouvindo o firehose\",\"id\":\"etapa-6-ouvindo-o-firehose\"},{\"title\":\"Etapa 7. Listando os status mais recentes\",\"id\":\"etapa-7-listando-os-status-mais-recentes\"},{\"title\":\"Etapa 8. Atualizações otimistas\",\"id\":\"etapa-8-atualizacoes-otimistas\"},{\"title\":\"Pensando em AT Proto\",\"id\":\"pensando-em-at-proto\"},{\"title\":\"Próximos passos\",\"id\":\"proximos-passos\"}],\"/guides/data-repos/en.mdx\":[{\"title\":\"Data Layout\",\"id\":\"data-layout\"},{\"title\":\"Identifier Types\",\"id\":\"identifier-types\"}],\"/guides/data-repos/ja.mdx\":[{\"title\":\"データ レイアウト\",\"id\":\"\"},{\"title\":\"識別子の種類\",\"id\":\"\"}],\"/guides/data-repos/ko.mdx\":[{\"title\":\"데이터 레이아웃\",\"id\":\"\"},{\"title\":\"식별자 유형\",\"id\":\"\"}],\"/guides/data-repos/pt.mdx\":[{\"title\":\"Layout de dados\",\"id\":\"layout-de-dados\"},{\"title\":\"Tipos de Identificadores\",\"id\":\"tipos-de-identificadores\"}],\"/guides/faq/en.mdx\":[{\"title\":\"Is the AT Protocol a blockchain?\",\"id\":\"is-the-at-protocol-a-blockchain\"},{\"title\":\"Why not use ActivityPub?\",\"id\":\"why-not-use-activity-pub\"},{\"title\":\"Why create Lexicon instead of using JSON-LD or RDF?\",\"id\":\"why-create-lexicon-instead-of-using-json-ld-or-rdf\"},{\"title\":\"What is “XRPC,” and why not use ___?\",\"id\":\"what-is-xrpc-and-why-not-use\"}],\"/guides/faq/ja.mdx\":[{\"title\":\"AT プロトコルはブロックチェーンですか?\",\"id\":\"at\"},{\"title\":\"ActivityPub を使用しないのはなぜですか?\",\"id\":\"activity-pub\"},{\"title\":\"JSON-LD や RDF を使用する代わりに Lexicon を作成する理由\",\"id\":\"json-ld-rdf-lexicon\"},{\"title\":\"「XRPC」とは何ですか。なぜ ___ を使用しないのですか?\",\"id\":\"xrpc\"}],\"/guides/faq/ko.mdx\":[{\"title\":\"AT Protocol은 블록체인인가요?\",\"id\":\"at-protocol\"},{\"title\":\"왜 ActivityPub을 사용하지 않나요?\",\"id\":\"activity-pub\"},{\"title\":\"왜 JSON-LD나 RDF 대신 Lexicon을 만들었나요?\",\"id\":\"json-ld-rdf-lexicon\"},{\"title\":\"\\\"XRPC\\\"란 무엇이며, 왜 ___을 사용하지 않나요?\",\"id\":\"xrpc\"}],\"/guides/faq/pt.mdx\":[{\"title\":\"O Protocolo AT é uma blockchain?\",\"id\":\"o-protocolo-at-e-uma-blockchain\"},{\"title\":\"Por que não usar o ActivityPub?\",\"id\":\"por-que-nao-usar-o-activity-pub\"},{\"title\":\"Por que criar o Lexicon em vez de usar JSON-LD ou RDF?\",\"id\":\"por-que-criar-o-lexicon-em-vez-de-usar-json-ld-ou-rdf\"},{\"title\":\"O que é “XRPC” e por que não usar ___?\",\"id\":\"o-que-e-xrpc-e-por-que-nao-usar\"}],\"/guides/glossary/en.mdx\":[{\"title\":\"Atmosphere\",\"id\":\"atmosphere\"},{\"title\":\"AT Protocol\",\"id\":\"at-protocol\"},{\"title\":\"PDS (Personal Data Server)\",\"id\":\"pds-personal-data-server\"},{\"title\":\"AppView\",\"id\":\"app-view\"},{\"title\":\"Relay\",\"id\":\"relay\"},{\"title\":\"Lexicon\",\"id\":\"lexicon\"},{\"title\":\"Data Repo\",\"id\":\"data-repo\"},{\"title\":\"Collection\",\"id\":\"collection\"},{\"title\":\"Record\",\"id\":\"record\"},{\"title\":\"Blob\",\"id\":\"blob\"},{\"title\":\"Label\",\"id\":\"label\"},{\"title\":\"Handle\",\"id\":\"handle\"},{\"title\":\"DID (Decentralized ID)\",\"id\":\"did-decentralized-id\"},{\"title\":\"NSID (Namespaced ID)\",\"id\":\"nsid-namespaced-id\"},{\"title\":\"TID (Timestamp ID)\",\"id\":\"tid-timestamp-id\"},{\"title\":\"CID (Content ID)\",\"id\":\"cid-content-id\"},{\"title\":\"DAG-CBOR\",\"id\":\"dag-cbor\"},{\"title\":\"XRPC\",\"id\":\"xrpc\"}],\"/guides/glossary/ja.mdx\":[{\"title\":\"Atmosphere\",\"id\":\"atmosphere\"},{\"title\":\"AT プロトコル\",\"id\":\"at\"},{\"title\":\"PDS (パーソナル データ サーバー)\",\"id\":\"pds\"},{\"title\":\"AppView\",\"id\":\"app-view\"},{\"title\":\"リレー\",\"id\":\"\"},{\"title\":\"Lexicon\",\"id\":\"lexicon\"},{\"title\":\"データ リポジトリ\",\"id\":\"\"},{\"title\":\"コレクション\",\"id\":\"\"},{\"title\":\"レコード\",\"id\":\"\"},{\"title\":\"BLOB\",\"id\":\"blob\"},{\"title\":\"ラベル\",\"id\":\"\"},{\"title\":\"ハンドル\",\"id\":\"\"},{\"title\":\"DID (分散 ID)\",\"id\":\"did-id\"},{\"title\":\"NSID (名前空間 ID)\",\"id\":\"nsid-id\"},{\"title\":\"TID (タイムスタンプ ID)\",\"id\":\"tid-id\"},{\"title\":\"CID (コンテンツ ID)\",\"id\":\"cid-id\"},{\"title\":\"DAG-CBOR\",\"id\":\"dag-cbor\"},{\"title\":\"XRPC\",\"id\":\"xrpc\"}],\"/guides/glossary/ko.mdx\":[{\"title\":\"Atmosphere\",\"id\":\"atmosphere\"},{\"title\":\"AT Protocol\",\"id\":\"at-protocol\"},{\"title\":\"PDS (Personal Data Server)\",\"id\":\"pds-personal-data-server\"},{\"title\":\"AppView\",\"id\":\"app-view\"},{\"title\":\"릴레이\",\"id\":\"\"},{\"title\":\"Lexicon\",\"id\":\"lexicon\"},{\"title\":\"데이터 레포지토리\",\"id\":\"\"},{\"title\":\"컬렉션\",\"id\":\"\"},{\"title\":\"레코드\",\"id\":\"\"},{\"title\":\"블롭\",\"id\":\"\"},{\"title\":\"라벨\",\"id\":\"\"},{\"title\":\"핸들\",\"id\":\"\"},{\"title\":\"DID (Decentralized ID)\",\"id\":\"did-decentralized-id\"},{\"title\":\"NSID (Namespaced ID)\",\"id\":\"nsid-namespaced-id\"},{\"title\":\"TID (Timestamp ID)\",\"id\":\"tid-timestamp-id\"},{\"title\":\"CID (Content ID)\",\"id\":\"cid-content-id\"},{\"title\":\"DAG-CBOR\",\"id\":\"dag-cbor\"},{\"title\":\"XRPC\",\"id\":\"xrpc\"}],\"/guides/glossary/pt.mdx\":[{\"title\":\"Atmosphere\",\"id\":\"atmosphere\"},{\"title\":\"AT Protocol\",\"id\":\"at-protocol\"},{\"title\":\"PDS (Personal Data Server)\",\"id\":\"pds-personal-data-server\"},{\"title\":\"AppView\",\"id\":\"app-view\"},{\"title\":\"Relay\",\"id\":\"relay\"},{\"title\":\"Lexicon\",\"id\":\"lexicon\"},{\"title\":\"Data Repo\",\"id\":\"data-repo\"},{\"title\":\"Collection\",\"id\":\"collection\"},{\"title\":\"Record\",\"id\":\"record\"},{\"title\":\"Blob\",\"id\":\"blob\"},{\"title\":\"Label\",\"id\":\"label\"},{\"title\":\"Handle\",\"id\":\"handle\"},{\"title\":\"DID (Decentralized ID)\",\"id\":\"did-decentralized-id\"},{\"title\":\"NSID (Namespaced ID)\",\"id\":\"nsid-namespaced-id\"},{\"title\":\"TID (Timestamp ID)\",\"id\":\"tid-timestamp-id\"},{\"title\":\"CID (Content ID)\",\"id\":\"cid-content-id\"},{\"title\":\"DAG-CBOR\",\"id\":\"dag-cbor\"},{\"title\":\"XRPC\",\"id\":\"xrpc\"}],\"/guides/identity/en.mdx\":[{\"title\":\"Identifiers\",\"id\":\"identifiers\"},{\"title\":\"DID Methods\",\"id\":\"did-methods\"},{\"title\":\"Handle Resolution\",\"id\":\"handle-resolution\"}],\"/guides/identity/ja.mdx\":[{\"title\":\"識別子\",\"id\":\"\"},{\"title\":\"DID メソッド\",\"id\":\"did\"},{\"title\":\"ハンドル解決\",\"id\":\"\"}],\"/guides/identity/ko.mdx\":[{\"title\":\"식별자\",\"id\":\"\"},{\"title\":\"DID 메서드\",\"id\":\"did\"},{\"title\":\"핸들 처리\",\"id\":\"\"}],\"/guides/identity/pt.mdx\":[{\"title\":\"Identificadores\",\"id\":\"identificadores\"},{\"title\":\"Métodos DID\",\"id\":\"metodos-did\"},{\"title\":\"Resolução de identificadores\",\"id\":\"resolucao-de-identificadores\"}],\"/guides/lexicon/en.mdx\":[{\"title\":\"Why is Lexicon needed?\",\"id\":\"why-is-lexicon-needed\"},{\"title\":\"HTTP API methods\",\"id\":\"http-api-methods\"},{\"title\":\"Record types\",\"id\":\"record-types\"},{\"title\":\"Tokens\",\"id\":\"tokens\"},{\"title\":\"Versioning\",\"id\":\"versioning\"},{\"title\":\"Schema distribution\",\"id\":\"schema-distribution\"}],\"/guides/lexicon/ja.mdx\":[{\"title\":\"Lexicon が必要な理由\",\"id\":\"lexicon\"},{\"title\":\"HTTP API メソッド\",\"id\":\"http-api\"},{\"title\":\"レコード タイプ\",\"id\":\"\"},{\"title\":\"トークン\",\"id\":\"\"},{\"title\":\"バージョン管理\",\"id\":\"\"},{\"title\":\"スキーマの配布\",\"id\":\"\"}],\"/guides/lexicon/ko.mdx\":[{\"title\":\"Lexicon이 필요한 이유\",\"id\":\"lexicon\"},{\"title\":\"HTTP API 메서드\",\"id\":\"http-api\"},{\"title\":\"레코드 유형\",\"id\":\"\"},{\"title\":\"토큰\",\"id\":\"\"},{\"title\":\"버전 관리\",\"id\":\"\"},{\"title\":\"스키마 배포\",\"id\":\"\"}],\"/guides/lexicon/pt.mdx\":[{\"title\":\"Por que o Lexicon é necessário?\",\"id\":\"por-que-o-lexicon-e-necessario\"},{\"title\":\"Métodos de API HTTP\",\"id\":\"metodos-de-api-http\"},{\"title\":\"Tipos de registro\",\"id\":\"tipos-de-registro\"},{\"title\":\"Tokens\",\"id\":\"tokens\"},{\"title\":\"Versionamento\",\"id\":\"versionamento\"},{\"title\":\"Distribuição de esquema\",\"id\":\"distribuicao-de-esquema\"}],\"/guides/overview/en.mdx\":[{\"title\":\"Identity\",\"id\":\"identity\"},{\"title\":\"Data Repositories\",\"id\":\"data-repositories\"},{\"title\":\"Network Architecture\",\"id\":\"network-architecture\"},{\"title\":\"Interoperation\",\"id\":\"interoperation\"},{\"title\":\"Achieving Scale\",\"id\":\"achieving-scale\"},{\"title\":\"Algorithmic choice\",\"id\":\"algorithmic-choice\"},{\"title\":\"Account portability\",\"id\":\"account-portability\"},{\"title\":\"Speech, reach, and moderation\",\"id\":\"speech-reach-and-moderation\"},{\"title\":\"Specifications\",\"id\":\"specifications\"}],\"/guides/overview/ja.mdx\":[{\"title\":\"アイデンティティ\",\"id\":\"\"},{\"title\":\"データ リポジトリ\",\"id\":\"\"},{\"title\":\"フェデレーション\",\"id\":\"\"},{\"title\":\"相互運用\",\"id\":\"\"},{\"title\":\"スケールの実現\",\"id\":\"\"},{\"title\":\"アルゴリズムの選択\",\"id\":\"\"},{\"title\":\"アカウントの移植性\",\"id\":\"\"},{\"title\":\"スピーチ、リーチ、モデレーション\",\"id\":\"\"},{\"title\":\"仕様\",\"id\":\"\"}],\"/guides/overview/ko.mdx\":[{\"title\":\"아이덴티티\",\"id\":\"\"},{\"title\":\"데이터 레포지토리\",\"id\":\"\"},{\"title\":\"네트워크 아키텍처\",\"id\":\"\"},{\"title\":\"상호운용성\",\"id\":\"\"},{\"title\":\"확장성 달성\",\"id\":\"\"},{\"title\":\"알고리즘 선택\",\"id\":\"\"},{\"title\":\"계정 이동성\",\"id\":\"\"},{\"title\":\"발언, 도달, 그리고 모더레이션\",\"id\":\"\"},{\"title\":\"사양\",\"id\":\"\"}],\"/guides/overview/pt.mdx\":[{\"title\":\"Identidade\",\"id\":\"identidade\"},{\"title\":\"Repositórios de dados\",\"id\":\"repositorios-de-dados\"},{\"title\":\"Federação\",\"id\":\"federacao\"},{\"title\":\"Interoperação\",\"id\":\"interoperacao\"},{\"title\":\"Alcançando escala\",\"id\":\"alcancando-escala\"},{\"title\":\"Escolha algorítmica\",\"id\":\"escolha-algoritmica\"},{\"title\":\"Portabilidade de conta\",\"id\":\"portabilidade-de-conta\"},{\"title\":\"Fala, alcance e moderação\",\"id\":\"fala-alcance-e-moderacao\"},{\"title\":\"Especificações\",\"id\":\"especificacoes\"}],\"/guides/self-hosting/en.mdx\":[{\"title\":\"Table of Contents\",\"id\":\"table-of-contents\"},{\"title\":\"Preparation for self-hosting PDS\",\"id\":\"preparation-for-self-hosting-pds\"},{\"title\":\"Open your cloud firewall for HTTP and HTTPS\",\"id\":\"open-your-cloud-firewall-for-http-and-https\"},{\"title\":\"Configure DNS for your domain\",\"id\":\"configure-dns-for-your-domain\"},{\"title\":\"Check that DNS is working as expected\",\"id\":\"check-that-dns-is-working-as-expected\"},{\"title\":\"Installer on Ubuntu 20.04/22.04 and Debian 11/12\",\"id\":\"installer-on-ubuntu-20-04-22-04-and-debian-11-12\"},{\"title\":\"Verifying that your PDS is online and accessible\",\"id\":\"verifying-that-your-pds-is-online-and-accessible\"},{\"title\":\"Creating an account using pdsadmin\",\"id\":\"creating-an-account-using-pdsadmin\"},{\"title\":\"Creating an account using an invite code\",\"id\":\"creating-an-account-using-an-invite-code\"},{\"title\":\"Using the Bluesky app with your PDS\",\"id\":\"using-the-bluesky-app-with-your-pds\"},{\"title\":\"Updating your PDS\",\"id\":\"updating-your-pds\"},{\"title\":\"Getting help\",\"id\":\"getting-help\"}],\"/guides/self-hosting/ja.mdx\":[{\"title\":\"目次\",\"id\":\"\"}],\"/guides/self-hosting/ko.mdx\":[{\"title\":\"목차\",\"id\":\"\"}],\"/guides/self-hosting/pt.mdx\":[{\"title\":\"Índice\",\"id\":\"indice\"}],\"/specs/account/en.mdx\":[{\"title\":\"Hosting Status\",\"id\":\"hosting-status\"},{\"title\":\"PDS Account Migration\",\"id\":\"pds-account-migration\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security Considerations\",\"id\":\"security-considerations\"},{\"title\":\"Future Work\",\"id\":\"future-work\"}],\"/specs/account/ko.mdx\":[{\"title\":\"호스팅 상태\",\"id\":\"\"},{\"title\":\"PDS 계정 이전\",\"id\":\"pds\"},{\"title\":\"사용 및 구현 가이드라인\",\"id\":\"\"},{\"title\":\"보안 고려사항\",\"id\":\"\"},{\"title\":\"향후 작업\",\"id\":\"\"}],\"/specs/at-uri-scheme/en.mdx\":[],\"/specs/at-uri-scheme/ko.mdx\":[],\"/specs/atp/en.mdx\":[{\"title\":\"Protocol Structure\",\"id\":\"protocol-structure\"},{\"title\":\"Protocol Extension and Applications\",\"id\":\"protocol-extension-and-applications\"},{\"title\":\"What Is Missing?\",\"id\":\"what-is-missing\"},{\"title\":\"Future Work\",\"id\":\"future-work\"}],\"/specs/atp/ko.mdx\":[{\"title\":\"프로토콜 구조\",\"id\":\"\"},{\"title\":\"프로토콜 확장 및 애플리케이션\",\"id\":\"\"},{\"title\":\"누락된 부분\",\"id\":\"\"},{\"title\":\"향후 작업\",\"id\":\"\"}],\"/specs/blob/en.mdx\":[{\"title\":\"Blob Metadata\",\"id\":\"blob-metadata\"},{\"title\":\"Blob Lifecycle\",\"id\":\"blob-lifecycle\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security Considerations\",\"id\":\"security-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/blob/ko.mdx\":[{\"title\":\"블롭 메타데이터\",\"id\":\"\"},{\"title\":\"블롭 수명 주기\",\"id\":\"\"},{\"title\":\"사용 및 구현 지침\",\"id\":\"\"},{\"title\":\"보안 고려 사항\",\"id\":\"\"},{\"title\":\"향후 변경 사항\",\"id\":\"\"}],\"/specs/cryptography/en.mdx\":[{\"title\":\"ECDSA Signature Malleability\",\"id\":\"ecdsa-signature-malleability\"},{\"title\":\"Public Key Encoding\",\"id\":\"public-key-encoding\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/cryptography/ko.mdx\":[{\"title\":\"ECDSA 서명 변조 가능성\",\"id\":\"ecdsa\"},{\"title\":\"공개 키 인코딩\",\"id\":\"\"},{\"title\":\"사용 및 구현 가이드라인\",\"id\":\"\"},{\"title\":\"향후 변경 가능성\",\"id\":\"\"}],\"/specs/data-model/en.mdx\":[{\"title\":\"Relationship With IPLD\",\"id\":\"relationship-with-ipld\"},{\"title\":\"Data Types\",\"id\":\"data-types\"},{\"title\":\"blob Type\",\"id\":\"blob-type\"},{\"title\":\"JSON Representation\",\"id\":\"json-representation\"},{\"title\":\"Link and CID Formats\",\"id\":\"link-and-cid-formats\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security and Privacy Considerations\",\"id\":\"security-and-privacy-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/data-model/ko.mdx\":[{\"title\":\"IPLD와의 관계\",\"id\":\"ipld\"},{\"title\":\"데이터 타입\",\"id\":\"\"},{\"title\":\"blob 타입\",\"id\":\"blob\"},{\"title\":\"JSON 표현\",\"id\":\"json\"},{\"title\":\"링크와 CID 형식\",\"id\":\"cid\"},{\"title\":\"사용 및 구현 지침\",\"id\":\"\"},{\"title\":\"보안 및 개인정보 보호 고려사항\",\"id\":\"\"},{\"title\":\"향후 변경 가능 사항\",\"id\":\"\"}],\"/specs/did/en.mdx\":[{\"title\":\"Blessed DID Methods\",\"id\":\"blessed-did-methods\"},{\"title\":\"AT Protocol DID Identifier Syntax\",\"id\":\"at-protocol-did-identifier-syntax\"},{\"title\":\"DID Documents\",\"id\":\"did-documents\"},{\"title\":\"Representation of Public Keys\",\"id\":\"representation-of-public-keys\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/did/ko.mdx\":[{\"title\":\"승인된(Blessed) DID 메서드\",\"id\":\"blessed-did\"},{\"title\":\"AT Protocol DID 식별자 문법\",\"id\":\"at-protocol-did\"},{\"title\":\"DID 문서\",\"id\":\"did\"},{\"title\":\"공개 키의 표현\",\"id\":\"\"},{\"title\":\"사용 및 구현 지침\",\"id\":\"\"},{\"title\":\"향후 변경 가능 사항\",\"id\":\"\"}],\"/specs/event-stream/en.mdx\":[{\"title\":\"Streaming Wire Protocol (v0)\",\"id\":\"streaming-wire-protocol-v0\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security and Privacy Considerations\",\"id\":\"security-and-privacy-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/event-stream/ko.mdx\":[{\"title\":\"스트리밍 와이어 프로토콜 (v0)\",\"id\":\"v0\"},{\"title\":\"사용 및 구현 가이드라인\",\"id\":\"\"},{\"title\":\"보안 및 프라이버시 고려사항\",\"id\":\"\"},{\"title\":\"향후 변경 가능 사항\",\"id\":\"\"}],\"/specs/handle/en.mdx\":[{\"title\":\"Handle Identifier Syntax\",\"id\":\"handle-identifier-syntax\"},{\"title\":\"Additional Non-Syntax Restrictions\",\"id\":\"additional-non-syntax-restrictions\"},{\"title\":\"Identifier Examples\",\"id\":\"identifier-examples\"},{\"title\":\"Handle Resolution\",\"id\":\"handle-resolution\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/handle/ko.mdx\":[{\"title\":\"핸들 식별자 문법\",\"id\":\"\"},{\"title\":\"추가적인 문법 외 제한사항\",\"id\":\"\"},{\"title\":\"식별자 예시\",\"id\":\"\"},{\"title\":\"핸들 해석\",\"id\":\"\"},{\"title\":\"사용 및 구현 가이드라인\",\"id\":\"\"},{\"title\":\"향후 변경 가능성\",\"id\":\"\"}],\"/specs/label/en.mdx\":[{\"title\":\"Schema and Data Model\",\"id\":\"schema-and-data-model\"},{\"title\":\"Value\",\"id\":\"value\"},{\"title\":\"Label Lifecycle: Negation and Expiration\",\"id\":\"label-lifecycle-negation-and-expiration\"},{\"title\":\"Signatures\",\"id\":\"signatures\"},{\"title\":\"Self-Labels in Records\",\"id\":\"self-labels-in-records\"},{\"title\":\"Labeler Service Identity\",\"id\":\"labeler-service-identity\"},{\"title\":\"Label Distribution Endpoints\",\"id\":\"label-distribution-endpoints\"},{\"title\":\"Labeler HTTP Headers\",\"id\":\"labeler-http-headers\"},{\"title\":\"Security Considerations\",\"id\":\"security-considerations\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/label/ko.mdx\":[{\"title\":\"스키마 및 데이터 모델\",\"id\":\"\"},{\"title\":\"값\",\"id\":\"\"},{\"title\":\"라벨 수명 주기: 무효화와 만료\",\"id\":\"\"},{\"title\":\"서명\",\"id\":\"\"},{\"title\":\"레코드 내 셀프 라벨\",\"id\":\"\"},{\"title\":\"라벨러 서비스 정체성\",\"id\":\"\"},{\"title\":\"라벨 배포 엔드포인트\",\"id\":\"\"},{\"title\":\"라벨러 HTTP 헤더\",\"id\":\"http\"},{\"title\":\"보안 고려사항\",\"id\":\"\"},{\"title\":\"사용 및 구현 가이드라인\",\"id\":\"\"},{\"title\":\"향후 변경 가능 사항\",\"id\":\"\"}],\"/specs/lexicon/en.mdx\":[{\"title\":\"Overview of Types\",\"id\":\"overview-of-types\"},{\"title\":\"Lexicon Files\",\"id\":\"lexicon-files\"},{\"title\":\"Primary Type Definitions\",\"id\":\"primary-type-definitions\"},{\"title\":\"Field Type Definitions\",\"id\":\"field-type-definitions\"},{\"title\":\"String Formats\",\"id\":\"string-formats\"},{\"title\":\"When to use $type\",\"id\":\"when-to-use-type\"},{\"title\":\"Lexicon Evolution\",\"id\":\"lexicon-evolution\"},{\"title\":\"Authority and Control\",\"id\":\"authority-and-control\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/lexicon/ko.mdx\":[{\"title\":\"타입 개요\",\"id\":\"\"},{\"title\":\"Lexicon 파일\",\"id\":\"lexicon\"},{\"title\":\"기본 타입 정의\",\"id\":\"\"},{\"title\":\"필드 타입 정의\",\"id\":\"\"},{\"title\":\"문자열 형식\",\"id\":\"\"},{\"title\":\"언제 $type을 사용할까\",\"id\":\"type\"},{\"title\":\"Lexicon 진화\",\"id\":\"lexicon-2\"},{\"title\":\"권한 및 제어\",\"id\":\"\"},{\"title\":\"사용 및 구현 지침\",\"id\":\"\"},{\"title\":\"향후 변경 가능성\",\"id\":\"\"}],\"/specs/nsid/en.mdx\":[],\"/specs/nsid/ko.mdx\":[],\"/specs/oauth/en.mdx\":[{\"title\":\"Clients\",\"id\":\"clients\"},{\"title\":\"Identity Authentication\",\"id\":\"identity-authentication\"},{\"title\":\"Authorization Scopes\",\"id\":\"authorization-scopes\"},{\"title\":\"Authorization Requests\",\"id\":\"authorization-requests\"},{\"title\":\"Tokens and Session Lifetime\",\"id\":\"tokens-and-session-lifetime\"},{\"title\":\"Demonstrating Proof of Possession (DPoP)\",\"id\":\"demonstrating-proof-of-possession-d-po-p\"},{\"title\":\"Authorization Servers\",\"id\":\"authorization-servers\"},{\"title\":\"Summary of Authorization Flow\",\"id\":\"summary-of-authorization-flow\"},{\"title\":\"Security Considerations\",\"id\":\"security-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/oauth/ko.mdx\":[{\"title\":\"클라이언트\",\"id\":\"\"},{\"title\":\"신원 인증\",\"id\":\"\"},{\"title\":\"권한 범위(Scopes)\",\"id\":\"scopes\"},{\"title\":\"인증 요청\",\"id\":\"\"},{\"title\":\"토큰 및 세션 수명\",\"id\":\"\"},{\"title\":\"DPoP(서명된 증거) 증명\",\"id\":\"d-po-p\"},{\"title\":\"인증 서버\",\"id\":\"\"},{\"title\":\"인증 흐름 요약\",\"id\":\"\"},{\"title\":\"보안 고려사항\",\"id\":\"\"},{\"title\":\"향후 변경 가능 사항\",\"id\":\"\"}],\"/specs/record-key/en.mdx\":[],\"/specs/record-key/ko.mdx\":[],\"/specs/repository/en.mdx\":[{\"title\":\"Repo Data Structure (v3)\",\"id\":\"repo-data-structure-v3\"},{\"title\":\"CAR File Serialization\",\"id\":\"car-file-serialization\"},{\"title\":\"Repository Diffs\",\"id\":\"repository-diffs\"},{\"title\":\"Security Considerations\",\"id\":\"security-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/repository/ko.mdx\":[{\"title\":\"레포지토리 데이터 구조 (v3)\",\"id\":\"v3\"},{\"title\":\"CAR 파일 직렬화\",\"id\":\"car\"},{\"title\":\"레포지토리 차이(diff)\",\"id\":\"diff\"},{\"title\":\"보안 고려사항\",\"id\":\"\"},{\"title\":\"향후 변경 가능 사항\",\"id\":\"\"}],\"/specs/sync/en.mdx\":[{\"title\":\"Synchronization Primitives\",\"id\":\"synchronization-primitives\"},{\"title\":\"Firehose\",\"id\":\"firehose\"},{\"title\":\"Reliable Synchronization\",\"id\":\"reliable-synchronization\"},{\"title\":\"Bootstrapping a Live Mirror\",\"id\":\"bootstrapping-a-live-mirror\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security Concerns\",\"id\":\"security-concerns\"},{\"title\":\"Future Work\",\"id\":\"future-work\"}],\"/specs/sync/ko.mdx\":[{\"title\":\"동기화 기본 요소\",\"id\":\"\"},{\"title\":\"Firehose\",\"id\":\"firehose\"},{\"title\":\"신뢰할 수 있는 동기화\",\"id\":\"\"},{\"title\":\"실시간 미러 부트스트래핑\",\"id\":\"\"},{\"title\":\"사용 및 구현 가이드라인\",\"id\":\"\"},{\"title\":\"보안 우려사항\",\"id\":\"\"},{\"title\":\"향후 작업\",\"id\":\"\"}],\"/specs/tid/en.mdx\":[{\"title\":\"TID Structure\",\"id\":\"tid-structure\"},{\"title\":\"TID Syntax\",\"id\":\"tid-syntax\"}],\"/specs/tid/ko.mdx\":[{\"title\":\"TID 구조\",\"id\":\"tid\"},{\"title\":\"TID 구문\",\"id\":\"tid-2\"}],\"/specs/xrpc/en.mdx\":[{\"title\":\"Lexicon HTTP Endpoints\",\"id\":\"lexicon-http-endpoints\"},{\"title\":\"Authentication\",\"id\":\"authentication\"},{\"title\":\"Service Proxying\",\"id\":\"service-proxying\"},{\"title\":\"Summary of HTTP Headers\",\"id\":\"summary-of-http-headers\"},{\"title\":\"Summary of HTTP Status Codes\",\"id\":\"summary-of-http-status-codes\"},{\"title\":\"Usage and Implementation Guidelines\",\"id\":\"usage-and-implementation-guidelines\"},{\"title\":\"Security and Privacy Considerations\",\"id\":\"security-and-privacy-considerations\"},{\"title\":\"Possible Future Changes\",\"id\":\"possible-future-changes\"}],\"/specs/xrpc/ko.mdx\":[{\"title\":\"Lexicon HTTP 엔드포인트\",\"id\":\"lexicon-http\"},{\"title\":\"인증\",\"id\":\"\"},{\"title\":\"서비스 프록시\",\"id\":\"\"},{\"title\":\"HTTP 헤더 요약\",\"id\":\"http\"},{\"title\":\"HTTP 상태 코드 요약\",\"id\":\"http-2\"},{\"title\":\"사용 및 구현 가이드라인\",\"id\":\"\"},{\"title\":\"보안 및 개인정보 보호 고려사항\",\"id\":\"\"},{\"title\":\"향후 변경 가능 사항\",\"id\":\"\"}]},\"children\":[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"$6\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"div\",null,{\"className\":\"absolute inset-0 -z-10 mx-0 max-w-none overflow-hidden\",\"children\":[\"$\",\"div\",null,{\"className\":\"absolute left-1/2 top-0 ml-[-38rem] h-[25rem] w-[81.25rem] dark:[mask-image:linear-gradient(white,transparent)]\",\"children\":[\"$\",\"div\",null,{\"className\":\"absolute inset-0 bg-gradient-to-r from-[#6fa5ff] to-[#9dc0fb] opacity-40 [mask-image:radial-gradient(farthest-side_at_top,white,transparent)] dark:from-[#0090f2]/30 dark:to-[#7588ff]/30 dark:opacity-100\",\"children\":[\"$\",\"svg\",null,{\"aria-hidden\":\"true\",\"className\":\"absolute inset-x-0 inset-y-[-50%] h-[200%] w-full fill-black/40 stroke-black/50 mix-blend-overlay dark:fill-white/2.5 dark:stroke-white/5\",\"children\":[[\"$\",\"defs\",null,{\"children\":[\"$\",\"pattern\",null,{\"id\":\":S1:\",\"width\":72,\"height\":56,\"patternUnits\":\"userSpaceOnUse\",\"x\":-12,\"y\":4,\"children\":[\"$\",\"path\",null,{\"d\":\"M.5 56V.5H72\",\"fill\":\"none\"}]}]}],[\"$\",\"rect\",null,{\"width\":\"100%\",\"height\":\"100%\",\"strokeWidth\":0,\"fill\":\"url(#:S1:)\"}],[\"$\",\"svg\",null,{\"x\":-12,\"y\":4,\"className\":\"overflow-visible\",\"children\":[]}]]}]}]}]}],[\"$\",\"div\",null,{\"className\":\"mx-auto flex h-full max-w-xl flex-col items-center justify-center py-16 text-center\",\"children\":[[\"$\",\"p\",null,{\"className\":\"text-sm font-semibold text-zinc-900 dark:text-white\",\"children\":\"404\"}],[\"$\",\"h1\",null,{\"className\":\"mt-2 text-2xl font-bold text-zinc-900 dark:text-white\",\"children\":\"Page not found\"}],[\"$\",\"p\",null,{\"className\":\"mt-2 text-base text-zinc-600 dark:text-zinc-400\",\"children\":\"Sorry, we couldn’t find the page you’re looking for.\"}],[\"$\",\"$Le\",null,{\"className\":\"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-blue-400/10 dark:text-blue-400 dark:ring-1 dark:ring-inset dark:ring-blue-400/20 dark:hover:bg-blue-400/10 dark:hover:text-blue-300 dark:hover:ring-blue-300 mt-8\",\"href\":\"/\",\"children\":[false,\"Back to docs\",[\"$\",\"svg\",null,{\"viewBox\":\"0 0 20 20\",\"fill\":\"none\",\"aria-hidden\":\"true\",\"className\":\"mt-0.5 h-5 w-5 -mr-1\",\"children\":[\"$\",\"path\",null,{\"stroke\":\"currentColor\",\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"d\":\"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9\"}]}]]}]]}]],\"notFoundStyles\":[],\"styles\":null}]}]}]}]}]}]\n"])</script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10