AT Protocol Curriculum
Phase 1 of a video curriculum on the AT Protocol (atproto): a federated protocol for large social applications where each account is a DID with a domain handle, all public data lives in a signed repository on a personal data server, every host feeds a verifiable firehose that relays merge and AppViews index, and apps, feeds, labels and OAuth sit on top. Idea map, one unit card and script per idea.

AT Protocol Curriculum

Phase 1 output under the Video Curriculum Guide. Every unit is one video plus one post and carries exactly one idea.

What the series is. The Authenticated Transfer Protocol "is a decentralized protocol for large-scale social web applications." The series teaches the model as atproto.com describes it on 2026-10-22: how an account is a DID with a domain handle, how all public data lives in a signed repository on a personal data server, how every host feeds a firehose that relays merge and AppViews index, and how feeds, labels, OAuth, migration and the new Spaces extension sit on that base. The audience is people who build social, local-first or federated software.

Recorded source statements. Every claim in the scripts rests on one of these, quoted from atproto.com (guides, specs, blog), web.plc.directory, datatracker.ietf.org or the Bluesky feed-generator README (read 2026-10-22).

    Overview: "The Authenticated Transfer Protocol, aka atproto, is a decentralized protocol for large-scale social web applications." "The AT Protocol has a federated network architecture, meaning that account data is stored on host servers, as opposed to a peer-to-peer model between end devices." "Federation was chosen to ensure the network is convenient to use and reliably available." "The three core services in our network are Personal Data Servers (PDS), Relays, and App Views." "Relays collect data updates from many servers in to a single firehose." "A global schemas network called Lexicon is used to unify the names and behaviors of the calls across the servers." "We assume that a Personal Data Server may fail at any time"; "The goal of the AT Protocol is to ensure that a user can migrate their account to a new PDS without the server's involvement"; "In the event their PDS disappears without notice, the user should be able to migrate to a new provider by updating their DID Document and uploading their data backup."

    FAQ: "It's not a blockchain nor does it use a blockchain." "Our solution for portability requires both signed data repositories and DIDs, neither of which are easy to retrofit into ActivityPub." "ActivityPub depends heavily on delivering messages between a wide network of small-to-medium sized nodes, which can cause individual nodes to be flooded with traffic and generally struggles to provide global views of activity."

    The AT Stack: "Personal data servers, or PDSes, are your home in the cloud." "The AT Protocol is architected in a 'big world with small world fallbacks' way, modeled after the open web itself." "Relays are not a necessary part of the specification." "Importantly, relays themselves do not interpret records in repositories; they simply store them temporarily and forward them to consumers." "Full-network relays track all repositories across the entire network"; "Partial-network relays might focus on specific communities, applications, or regions." "You can host your own PDS." "PDSs implement many AT endpoints, and by default will proxy requests to other services."

    Distsys article: "we need to write programs which generate precomputed views of our dataset. These views are essentially like cached queries." "We'll call these our View servers."

    Glossary: "A PDS will always store the user's data repo and signing keys." "It's called an 'AppView' because it's just one view of the network. The canonical data lives in data repos which is hosted by PDSes, and that data can be viewed many different ways." "AppViews function a bit like search engines on the Web: they aggregate data from across the Atmosphere to produce their UIs." "Relays are an optimization and are not strictly necessary." "DIDs resolve to documents which contain metadata about a repo, including the address of the repo's PDS, the repo's handles, and the public signing keys."

    Identity guide: "We use two interrelated forms of identifiers: handles and DIDs. Handles are DNS names while DIDs are a W3C standard"; "These IDs should never change, to ensure that links to their content are stable." "The DNS handle is a user-facing identifier"; "Applications resolve handles to DIDs and then use the DID as the canonical identifier for accounts." "Any DID can be rapidly resolved to a DID document which includes public keys and user services." "Users must be able to rotate their key material without disrupting their identity." Handles "are resolved using DNS TXT records or an HTTP well-known endpoint, and must be confirmed by a matching entry in the DID document."

    Handle spec: "Handles must be bi-directionally linked to a DID: the DID document must claim the handle, and the handle must resolve to the DID identifier." "Two methods of resolving handles to DID are supported: one using TXT DNS records, and the other using an HTTPS /.well-known/ URL path." "The link between handle and DID must be confirmed bidirectionally, otherwise anybody could create handle aliases for third-party accounts." "a DNS TXT record is registered for the atproto sub-domain under the handle hostname."

    DID spec: "Currently, atproto supports two DID methods: did:plc: a self-authenticating DID method developed specifically for use with atproto" and "did:web: a W3C community draft based on HTTPS (and DNS)." "The DID Web method is inherently tied to the domain name used, and does not provide a mechanism for migration or recovering from loss of control of the domain name."

    did:plc spec (v0.3.0, December 2025): "DID PLC is a self-authenticating DID which is strongly-consistent, recoverable, and allows for key rotation." "Control over a did:plc identity rests in a set of reconfigurable rotation keys pairs. These keys can sign update operations to mutate the identity (including key rotations), with each operation referencing a prior version of the identity state by hash." "Each identity starts from an initial genesis operation, and the hash of this initial object is what defines the DID itself." "A central directory server collects and validates operations, and maintains a transparent log of operations for each DID." Rotation keys are "not included in DID document"; verificationMethods map services to public keys.

    Blog, PLC Directory Organization (19 Sept 2025): "Bluesky Social PBC is supporting the creation of an independent organization to operate the directory"; "the new entity will form as a Swiss Association"; PLC "will be developed and operated as a vendor- and application-neutral public good." "We do not expect this organization to be the final governance structure for PLC."

    Blog, PLC Read Replicas (18 Feb 2026): "a reference implementation of a PLC directory read-replica service"; "A read replica is a service that maintains a full, independently-queryable copy of the PLC directory's data by syncing from the primary instance"; it "should audit the synced data in real-time - verifying all operation hashes, signatures, and timestamp constraints."

    Data repositories guide: "A data repository is a collection of data published by a single user." "Repositories are self-authenticating data structures, meaning each update is signed and can be verified by anyone." "The content of a repository is laid out in a Merkle Search Tree (MST) which reduces the state to a single root hash." "Every node is an IPLD object (dag-cbor) which is referenced by a CID hash." "This chains all the way to the Commit node, which is then signed." "A 'commit' to a data repository is simply a keypair signature over a Root node's CID." Collections are identified by NSIDs; "some collections having only a single record with a key like 'self', and other collections having many records, with keys using a base32-encoded timestamp called a Timestamp Identifier (TID)."

    Repository spec: "Repository contents are entirely public and verifiable ('self-certifying')." "a repository is a key/value mapping where the keys are path names (as strings) and the values are records (CBOR objects)." "Repo paths currently have a fixed structure of <collection>/<record-key>." "Commits are cryptographically signed, with rotatable signing keys, which allows recursive authentication of either the repository structure as a whole, or compact 'proof chains' for individual records." "Complete repositories can be exported as CAR files for synchronization, offline backup, account migration, or other purposes." Commit fields: "did (string, required): the account DID associated with the repo"; "version (integer, required): fixed value of 3"; "data (CID link, required): pointer to the top of the repo contents tree structure (MST)"; "rev (string, TID format, required): revision of the repo, used as a logical clock"; "prev (CID link, nullable): pointer (by hash) to a previous commit object"; "sig (byte array, required): cryptographic signature of this commit." The bytes are "hashed with SHA-256, and the binary hash output ... is then signed using the current 'signing key' for the account." "Record deletion is supported without leaving a trace or 'tombstone'."

    Data model spec: "When data needs to be authenticated (signed), referenced (linked by content hash), or stored efficiently, it is encoded in Concise Binary Object Representation (CBOR)." "The specific normalized subset of CBOR used in the atproto data model is called DRISL (which is successor to DAG-CBOR)." Nodes "can also reference each other strongly by hash, referred a link." Blob CIDs: "CID version: 1", "Hash type: sha-256".

    AT URI spec: "The AT URI scheme (at://) makes it easy to reference individual records in a specific repository, identified by either DID or handle." Example: at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.feed.post/3k5nobkf2w72g. "The authority part of the URI can be either a handle or a DID." "AT URIs referencing handles are not durable." "AT URIs are not content-addressed, so the contents of the record they refer to may also change over time."

    NSID spec: "The basic structure and semantics of an NSID are a fully-qualified hostname in reverse domain-name order, followed by an additional name segment." "The hostname part is the domain authority, and the final segment is the name."

    Record key spec: "Record Key Type: tid This is the most common record naming scheme, using Timestamp Identifier (TID) syntax, such as 3jzfcijpj2z2a." "Record Key Type: literal:<value> This key type is used when there should be only a single record in the collection... The most common value is self." Key types also include nsid and any.

    TID spec: "A Timestamp Identifier (TID) is a compact string identifier, based on a 64-bit integer, which can also be interpreted as a timestamp." "They are sortable, appropriate for use in web URLs, and useful as a 'logical clock' in networked systems." "length is always 13 ASCII characters"; "The next 53 bits represent microseconds since the UNIX epoch."

    Lexicon spec: "Lexicon is a schema definition language used to describe atproto records, HTTP endpoints (XRPC), and event stream messages." "Lexicons are JSON files associated with a single NSID." Primary types: "query: describes an XRPC Query (HTTP GET) procedure: describes an XRPC Procedure (HTTP POST) subscription: Event Stream (WebSocket) record: describes an object that can be stored in a repository record permission-set: describes a bundle of auth permissions." "Lexicon schemas are published publicly as records in atproto repositories, using the com.atproto.lexicon.schema type." "The authority for a Lexicon is determined by the NSID, and rooted in DNS control of the domain authority." "The primary mechanism for resolving protocol disputes is to fork Lexicons in to a new namespace." Resolution from an NSID to the publishing DID goes through a DNS TXT record on the authority domain (the spec's "Lexicon Publication and Resolution" section).

    XRPC spec: "The HTTP request path starts with /xrpc/, followed by an NSID." "The two requests types that can be expressed in Lexicons are 'query' (HTTP GET) and 'procedure' (HTTP POST)." "queries (GET) are cacheable and should not mutate resource state. Procedures are not cacheable and may mutate state." "Clients can use an HTTP header to specify which service in the network they want the request forwarded to (eg, a specific AppView or Labeler service). The PDS will do some safety checks, then forward the request on with an inter-service authentication token (JWT, described above) issued and signed by the user's identity." "The HTTP header is atproto-proxy, and the value is a DID (identifying a service), followed by a service endpoint identifier." "atproto-accept-labelers: used by clients to request labels from specific labelers to be included and applied in the response." App passwords "grant slightly restricted permissions to the client application, preventing destructive actions like account or changes to authentication settings."

    Blob spec: "Blobs are referenced by individual records by the blob lexicon datatype, which includes a content hash (CID) for the blob." "Blob metadata also includes the size of the blob (in bytes), and the MIME type." "Blobs must be uploaded to the PDS before a record can be created referencing that blob." "After a successful upload, blobs are placed in temporary storage." "Servers should 'garbage collect' (delete) un-referenced temporary blobs after an appropriate time span." "When a record referencing blobs is deleted, the server checks if any other current records from the same repository reference the blob. If not, the blob is deleted along with the record." "the server does not know the intended Lexicon when receiving an upload, so can only apply generic blob limits and restrictions at initial upload time."

    Sync spec: "Real-time synchronization can be provided by a repository event stream (over WebSocket), commonly referred to as a 'firehose'." "A repository event stream ('firehose') provides real-time updates about changes to repository state (#commit and #sync events), DID documents and handles (#identity events), and account hosting status (#account events)." "PDS hosts provide a firehose that includes updates for all hosted accounts." "Relays are network services which subscribe to multiple upstream firehoses (eg, multiple PDS hosts) and aggregate them in to a single combined event stream." "A relay which attempts to aggregate accounts from all PDS instances in the network outputs a 'full-network' firehose." "Repository data synchronized over a firehose is self-certifying and contains verifiable signatures. Consuming services can verify synchronized data without making additional requests to the account's PDS host." "Identity and account information is not self-certifying, and consuming services are responsible for verifying it." "Missing updates to a single repository are detectable as gaps in the stream of #commit messages." Commit events carry "prevData (cid-link, semi-optional): the root CID of the MST tree for the last commit" which is "Effectively required for MST inversion"; ops have "action (string, required)... One of: 'create', 'update', or 'delete'." "Synchronizing services should reject or ignore repository updates with revision values corresponding to future timestamps." "This generally means that static repository snapshots should not be redistributed publicly in bulk form."

    Event stream spec: "The initial encoding and transport scheme uses binary DRISL-CBOR encoding over WebSockets." "this works by assigning monotonically increasing sequence numbers to stream events, and allowing clients to specify an initial sequence number when initiating a connection." "Every WebSocket frame contains two CBOR objects... a header (indicating message type), and the actual message."

    Blog, Relay Updates for Sync v1.1 (May 2025): "The biggest change is that the relay no longer mirrors full repository data for all accounts in the network: sync v1.1 relays are now 'non-archival'." "The relay can handle thousands of messages per second using on the order of 2 vCPU cores, 12 GByte of RAM, and 30 Mbps." "As a final stage, we will re-configure both instances to enforce MST inversion more strictly and drop #commit messages which fail validation." "the relays currently attempt strict validation on every message and log failures (even if they are passed through on the firehose in 'lenient' mode)."

    Blog, Protocol check-in Fall 2025: "We're continuing to roll out Sync1.1 on the producer (Relay) side, but a fully spec-compliant consumer for it still doesn't exist." "Blacksky currently runs a full-network relay (using the rksy-relay implementation!), and is working on a full-network Bluesky appview."

    Blog, Jetstream: "an alternative streaming solution, Jetstream, which has a few key advantages: simple JSON encoding reduced bandwidth, and compression ability to filter by collection (NSID) or repo (DID)." "Events do not include cryptographic signatures or Merkle tree nodes, meaning the data is not self-authenticating." "The trust relationship between a Jetstream operator and a consuming client is pretty different from that of a Relay." "If you are running Jetstream locally, or have a tight trust relationship with a service provider, these may be acceptable tradeoffs."

    Blog, Introducing Tap (Dec 2025): "For many use cases – feed generators, labelers, bots – streaming live data through a Relay or Jetstream works well. But some applications need to go beyond what Jetstream was designed for, like tracking specific subsets of a repo, automatically backfilling a database when adding new repos to monitor, or even mirroring the entire Atmosphere." "Tap is a single-tenant service, written in Go, that subscribes to a Relay and outputs filtered, verified events of repositories." "When you begin tracking a repository, Tap fetches its complete history before delivering live events." "Tap handles MST integrity checks, identity signatures, and all the cryptographic validation required by the protocol."

    Feed generator README (bluesky-social/feed-generator): a feed generator answers getFeedSkeleton with "something that satisfies the SkeletonFeedPost[] type"; "Those posts are then hydrated into full views by the requesting server and sent back to the client." "The service itself is identified by DID, while each algorithm that it hosts is declared by a record in the repo of the account that created it." "This declaration record includes a pointer to the service's DID along with some profile information for the feed." "The service must be set up to respond to HTTPS queries over port 443."

    Label spec: "Labels primarily consist of a source (DID), a subject (URI), and value. The value is a short string, similar to a tag or hashtag." "One of the original design motivations was to enable some forms of composable moderation, with labels generated by moderation services." "Labels are signed using public-key Cryptography, similar to repository commit objects." "The key used for signing labels is found in the DID document for the issuing identity, and has fragment identifier #atprotolabel." "neg (boolean, optional): if true, indicates that this label 'negates' an earlier label." Services "may 'hydrate' labels in to requests from clients"; "It is assumed that most end-clients will not validate signatures themselves." Example "!warn as a behavior, as opposed to scam as a descriptive label which might result in the same warning behavior."

    Moderation guide: "The 'speech' layer should remain permissive, distributing authority and designed to ensure everyone has a voice. The 'reach' layer lives on top, built for flexibility and designed to scale." "Atproto moderation is implemented using Labels." "you can subscribe to moderation labelers to have their labels applied in your application. You can also create your own labeler to publish labels."

    Account spec: "On any network service, a known account is either active or not (a boolean flag)." "If an account is not active, the service should not redistribute content for that account." States: "deleted", "deactivated (active is false): user has temporarily paused their overall account"; "Also the initial state for an account after migrating to another PDS instance"; "takendown (active is false): host or service has takendown the account." "Identities may migrate between hosting providers."

    Account migration guide: "An identity on the atproto network can have multiple accounts on different PDSs, but only one account can be the identity's active account signing new records." Creating the new account uses "a service auth token (JWT) signed with the current atproto signing key"; "For an independently-controlled identity (eg, did:web, or a did:plc with the old PDS offline or uncooperative), this may involve updating the identity to include a self-controlled atproto signing key"; this is "an 'Adversarial Migration' when the old PDS is offline or uncooperative." "The new account will be in a deactivated state." "A copy of the repository can be fetched as a CAR file from the old PDS using the public com.atproto.sync.getRepo endpoint." Data migrated: "public repository public blobs (media files) private preferences." "'Recommended' DID document parameters can be fetched from the new PDS using the com.atproto.identity.getRecommendedDidCredentials endpoint." "The migration of the identity to pointing to the new account on the PDS is not finalized till the change on the identity's did document is made."

    OAuth spec: "Atproto uses a specific 'profile' of OAuth which mandates a particular combination of OAuth standards." "The clientid is a fully-qualified web URL pointing to the public client metadata (JSON document). There is no clientsecret shared between servers and clients." "When initiating a login with a handle or DID, an atproto-specific identity resolution step is required to discover the account's PDS network location." "Sometimes the PDS is also the 'Authorization Server'... while in other situations a separate 'entryway' service acts as the Authorization Server for multiple PDS instances." "PKCE and PAR are required for all client types and Authorization Servers." "DPoP (with mandatory server issued nonces) is required to bind auth tokens to specific client software instances (eg, end devices or browser sessions)." "Pushed Authentication Requests (PAR) are used to streamline the authorization request flow." "The special atproto scope is required for all atproto OAuth sessions." "OAuth scopes allow more granular control over the resources and actions a client is granted access to." "As of October 2026, client metadata is still a draft (draft-ietf-oauth-client-id-metadata-document)."

    Permissions spec: "Control over atproto resources is described and granted using 'permissions'." "A group of a permissions related to a specific Lexicon namespace (record types and API endpoints) can be bundled together as a 'permission set'." "A permission can be represented in a string format (for direct use as an OAuth scope), or as a JSON object (for use in permission sets)." Resource types: "repo: Public Repository (records and collections) rpc: Service Authentication (API calls to external services) blob: uploaded media files identity: DID and handle account: hosting status, email address"; "an additional 'meta' resource, include, which is used to reference a permission set." "Permission sets are published as public lexicon schemas, and are constrained in scope to named resources under the same NSID hierarchy as the NSID of the permission set itself." "Resources and permissions are described in the abstract, not tied to specific PDS API endpoints." Example repo permission: "collection (array of strings, required, positional): NSID of record types."

    Blog, Atproto Spaces alpha (20 Aug 2026, Daniel Holmgren): "The biggest update to atproto since it first launched is available as an alpha." "Recall that, by design, all data stored on the protocol today is public — every post, every follow, every like, every block." "Spaces, a new protocol primitive, provide a way to store and sync non-public data while retaining the advantages of atproto like portable identity, interoperable/remixable data, and permissionless participation." "You can think of an atproto space as a miniature atproto network that can be gated so that only certain people and applications are able to access the data published in it." "Access to a space is controlled by a space authority, which is just a DID like any other account." "Records live in per-space permissioned repos on the author's PDS." "It's important to remember that spaces give you access control not confidentiality. The data in a space is readable by any user or application with access to that space, it's not encrypted." "there is no concept of a relay for data stored in a space. ... Applications will sync space data directly from PDS hosts." "This is truly an alpha." "There will be breaking changes, and you absolutely should not run production code against it."

    Blog, Introducing Hubble (20 Mar 2026, guest post by fig, microcosm): "Hubble is a new open-source project to build and operate a whole-Atmosphere public data mirror, synchronizing every atproto repository in real-time, keeping public data available even when a PDS goes down." "In the bad old days of early Atmosphere, relays stored a local copy of every user's data repository"; "All relays in the network now run this way, and are called non-archival: lightweight and cheap to run, but only retaining recent events, not the full history of the network." "unless you can find a copy from somewhere, you are simply out of luck." "a $20,000 grant from Bluesky for Hubble's development and one year of operation." "No blobs: Images, videos, and other media live separately from repos." "Hubble will fully respect content deletion. Deleted records are deleted from the mirror, and deleting your Atmosphere account removes your entire data repository." "account recovery for users, data availability for apps, and complete snapshots for researchers." "It takes a lot of storage to keep 43+ million repositories."

    Blog, Kicking off the ATP Working Group (2 Apr 2026): "an Authenticated Transfer Protocol (ATP) working group has been created at the IETF!" "The charter sets out the initial scope of work for writing standards documents: the public repository data structure mechanism for synchronizing public repositories (e.g., the firehose) the AT URI scheme requirements for account identifier resolution systems, and a registry of recommended systems (e.g., PLC and did:web)." "this initial charter does not include non-public data, application-specific data schemas or APIs, lexicon publication, the labeling moderation system, etc." The BoF was at "IETF 124 meeting in Montreal" in November 2025.

    Blog, Spring 2026 roadmap: "in December we released tap, a self-hosted atproto sync consumer, which serves as a reference implementation for Sync 1.1." "OAuth Completeness: permissions and permission sets shipped." "WebSocket support for streaming updates was added in early January, which made it easier to implement live PLC replicas." "Permissioned data will probably be a major focus for the Bluesky protocol team through the summer." "Any major changes to synchronizing public data are expected to take place within the IETF standards process."

    Self-hosting guide: "Start here if you want to host your own PDS!" "Hosting your own Relay is possible, though bandwidth intensive." "Hosting your own AppView is possible, but resource intensive."

    web.plc.directory (read 2026-10-22): "It does not make use of any blockchain or cryptocurrency technology." "In 2026 the PLC Organization was founded to be a neutral steward for the DID PLC system." plcred.org: "The PLC Organization is a small Swiss Association ('Verein') which provides open network identifier infrastructure." "The Organization was established in early 2026, and is in the process of developing policies and building operational capacity."

    did:plc spec, recovery: "The PLC server provides a 72hr window during which a higher authority rotation key can 'rewrite' history, clobbering any operations (or chain of operations) signed by a lower-authority rotation key." Overview: "The signing key is entrusted to the PDS so that it can manage the user's data, but rotation keys can be controlled by the user, e.g. as a paper key." The AT Stack, PDS duties: "secret key management: AT signing key, and PLC rotation key".

    AT URI spec, references: "An AT URI pointing to a specific record in a repository is not a strong reference, in that it is not content-addressed." Lexicon com.atproto.repo.strongRef: "A URI with a content-hash fingerprint", with uri and cid both required.

    Repository spec, blobs: "Larger binary media files ('blobs') are also referenced by content hash, but are not stored directly in the repository." Lexicon spec, blob type: "accept (array of strings, optional): list of acceptable MIME types"; "maxSize (integer, optional): maximum size in bytes".

    Sync spec, #identity: "Does not indicate what changed, or reliably indicate what the current state of the identity is."

    Blog, Upcoming Relay Transition (24 Jan 2026): "we are finally going to transition the bsky.network firehose endpoint to a newer relay implementation"; "However, strict verificaiton will still be disabled for now. We do intent to enable strict MST verification in the near future." (spelling as published). The AT Stack: "A full network relay isn't particularly expensive to operate, and doesn't require specialized hardware, though it does require significant ingress and egress network bandwidth, which is the main cost in operating a relay."

    Blog, Introducing Bluesky Protocol Services (13 Aug 2026): "Jetstream v2: Network Replay"; "What it couldn't give you was history. Jetstream v2 adds that capability to the server. It keeps a compressed archive of the whole network and adds a new way to consume it, alongside the live tail: Network Replay lets you catch up from any point in the past and cut over to live with no gap."

    Label lexicon com.atproto.label.defs#labelValueDefinition: "Declares a label value and its expected interpretations and behaviors."; severity: "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing."; blurs: "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing."; defaultSetting: "The default setting for this label." Label spec: "The behavior, definition, meaning, and policies around labels are generally communicated elsewhere."

    OAuth spec, tokens: "Tokens are always bound to a unique session DPoP key."

    Permissions spec, blob, account and identity resources: "Permissions of this type can not be included in permission sets, and must be requested directly by client apps."

    Account migration guide, finishing: "However, the new account is not yet 'active'." "The user can activate their account on the new PDS with a call to com.atproto.server.activateAccount, and deactivate their account on the old PDS with com.atproto.server.deactivateAccount. At this point the migration is complete." Account spec: "updating account status on both PDS instances, such that old is inactive, and new is active." Hubble post: "It's easier to move when your PDS is online, but with a rotation key and a backup of your data, you're in complete control."

    Blog, Developer Experience Goals for Atproto Spaces (14 Oct 2026): "Released a couple rounds of breaking changes"; "We expect to launch atproto Spaces this year. And that means that we're starting to lock in the core primitives for Spaces, because we're confident that most pieces are going to stop churning from here." "Spaces sync without a relay." "Rather, each space functions as a sync boundary." Blog, An Intro to Atmospheric Groups (22 Oct 2026): "An atmospheric group is a DID with a bunch of permissioned spaces under it."

    Blog, Spring 2026 roadmap, on sync: "There are a few remaining pieces of Sync 1.1 to implement: defined block ordering for repository CAR file exports, and exporting subsets of repositories by collection."

    Feed generator README, hydration: "Those posts are then hydrated into full views by the requesting server and sent back to the client." The AT Stack: "App Views are application-level tooling, rather than inherent parts of the AT protocol."

Scope. The protocol as atproto.com describes it, plus the 2025 to 2026 changes to sync, identity governance, standards and private data. Not covered: the Bluesky application schemas in detail (app.bsky.), the cryptography page (key types and encodings), inter-service JWT details, legacy session tokens, admin auth, Ozone and Osprey, the SDKs, and comparisons with ActivityPub beyond the FAQ's own words.

Status. Revision 2, 2026-10-22, after the independent text review. Twenty-four accuracy findings and ten structure findings were resolved: three wrong claims fixed (the PLC Organization already exists; your own host can forge you; blob limits are the schema's too), four stale facts brought to October 2026, unit 4 given the recovery window its title promises, the fifth Lexicon kind named, activation added to migration, fifteen metaphors now spoken. See "Review notes".

Idea map

    The AT Protocol: a social web you can leave. Federated, not peer-to-peer and not a blockchain; one shared stream of public data; identity, repositories, sync, apps. needs: nothing

    Your account is a DID; your handle is a domain. Two names; the DID is permanent; handles resolve by DNS TXT or well-known file and must be confirmed both ways. needs: 1

    The DID document: a key, a handle, and an address. Signing key, handle claim, PDS address; two methods, did:web and did:plc. needs: 2

    did:plc: an identity you can recover. A ledger of signed operations whose genesis hash is the DID; rotation keys above the signing key install a new one; a central directory, not a blockchain; for three days a higher rotation key can undo a change. needs: 3

    A repository is a signed set of records. Records in collections; a Merkle search tree with one root; every version signed; self-authenticating. needs: 3

    Every record has an address. at://did/collection/rkey; NSIDs are reversed domains; TIDs; self; resolution through the DID. needs: 5

    Records are hashed, and links are hashes. DRISL-CBOR, SHA-256 CIDs as fingerprints; inside the repository every link is a hash; between records a reference is an address with the CID beside it; hashes up to the root. needs: 5

    A commit is a signed root with a clock. Commit fields; rev as logical clock; MST locality; diffs are a few blocks; signed with the DID's key, which your own host holds. needs: 7

    Lexicon: schemas with a domain in their name. NSIDs; record, query, procedure, subscription, permission-set; published as records; any app can read any app's records. needs: 6

    XRPC: every API call is a Lexicon method over HTTP. /xrpc/nsid; GET for queries, POST for procedures; typed; the atproto-proxy header; one method name across servers. needs: 9

    The PDS is your home: it holds your repo and your keys. Stores repo and signing keys; answers clients; proxies; publishes commits; self-hostable. needs: 8, 10

    Blobs ride beside the repository. Upload first, temporary storage, record references by CID, type and size; garbage collection; host limits. needs: 11

    The firehose: every change, in order, signed. Per-host event stream; #commit with blocks; #identity and #account; sequence numbers and cursors; binary WebSocket. needs: 8, 11

    A relay is a crawler that re-broadcasts. Subscribes to many hosts, merges, verifies, non-archival since sync v1.1, anyone can run one, full or partial. needs: 13

    An AppView is a materialized view. Index built from the stream; counts, feeds, search; one view among many; new apps can index old records; write to PDS, read from AppView. needs: 14

    Jetstream and Tap: lighter ways to drink. Jetstream drops signatures, filters, JSON, and since v2 replays the past; Tap keeps verification and fetches the past from the host; choose by how much to verify. needs: 13

    A feed is a service, not a setting. In Bluesky, a feed generator with a DID; skeleton of post URIs; the app's server hydrates; declared by a record in the author's repo. needs: 15

    Labels: moderation as a subscribable layer. Source, subject, value; signed by a labeler key; you choose labelers; app applies behavior; takedowns are a different lever. needs: 15

    OAuth: apps log in without your password. Client metadata URL, no secret; login at your PDS; PAR, PKCE, DPoP required; app passwords as the old way. needs: 11

    Permissions name a collection, not an account. repo, rpc, blob, account, identity; permission sets as Lexicons; abstract resources. needs: 19

    Moving house without asking permission. Export CAR and blobs; import at the new host; new keys; update the DID document; activate the new, deactivate the old; adversarial migration with a rotation key you kept. needs: 4, 11, 12, 13

    Public by default; Spaces is the private extension. All data public by design; a space is a gated mini-network; per-space permissioned repos on the author's PDS; access control not confidentiality; alpha. needs: 11, 14

    Who keeps the copy: non-archival relays and Hubble. Relays keep no archive; a dead host loses its data unless you kept a backup; Hubble is a public mirror of every repository; no media; deletions respected. needs: 14, 16, 21

    Where atproto stands. IETF ATP working group and its scope; the PLC Organization, founded 2026, and replicas; sync v1.1 on the relays with pieces to come; Spaces launch expected. needs: all

Shared visual language

    Account. An id card with a did:plc: number and a domain tag for the handle.

    DID document. A page with three rows: key, handle, address.

    Repository. A ledger box of record cards sorted into collections; a hash tree above it with one root; a seal on the root for the commit.

    PDS. A house with a server in it: the home.

    Stream. A pipe of event cards leaving a host; relays as funnels joining pipes; the AppView as an index building from the pipe.

    Record address. A monospace URL with the three parts colored: DID, collection, key.

    Blob. A parcel beside the ledger box.

    Label. A sticker on a card.

    Scope. A monospace chip: resource, colon, the named thing.

    Colors: amber for the thing being introduced, green for verified or allowed, purple for keys and signatures, muted for what the user does not see or what is gone.

Units

Unit 1: The AT Protocol: a social web you can leave


    Idea. Atproto is a federated protocol for large social applications whose founding promise is that you can leave with your account; data lives on host servers, every host feeds one shared public stream, and it is not a blockchain.

    Why it matters. It frames the whole series: the design choices follow from portability and a global view.

    Context assumed. Nothing.

    Metaphor. A bag of data you can carry between servers.

    Misconception. Federated means a web of small islands with no global view. Addressed at beat 3.

    Visual language. User with a bag, two servers; hosts feeding one stream; four blocks.

    Target length. 50 to 60 seconds.

    Check question. Where does your data live, and who can read the shared stream?

Script:

    The Authenticated Transfer Protocol, atproto, is a protocol for large social applications. Its founding promise: your account and its data are a bag you can carry to another server. (Screen: a user with a bag of data standing between two servers.)

    It is federated. Your data lives on a host server, not on your phone. It is not a blockchain, and does not use one. (Screen: data on host servers; no peer-to-peer mesh; no chain.)

    But it is not a web of small islands either. Every host feeds one big shared stream, and apps build their views from it. (Screen: many hosts feeding one big stream.)

    Everything in that stream is public. Posts, likes, follows: signed, and readable by any app. (Screen: a record visible to everyone.)

    This series takes it apart one idea at a time. Identity. Repositories. Sync. And the apps on top. (Screen: four blocks: identity, repos, sync, apps.)

Unit 2: Your account is a DID; your handle is a domain


    Idea. An account has a permanent DID and a human-readable domain handle; the handle resolves to the DID by a DNS TXT record or a well-known file, and the DID document must claim the handle back.

    Why it matters. Stable links and portable names rest on this two-way link.

    Context assumed. Unit 1.

    Metaphor. An id number and a name tag.

    Misconception. The handle is the identity. Addressed at beat 2.

    Visual language. Id card, domain tag, DNS record, well-known file, two arrows.

    Target length. 50 to 60 seconds.

    Check question. Why must the link between handle and DID be confirmed in both directions?

Script:

    An account has two names. A DID, a decentralized identifier: a permanent id number. And a handle, a domain name: the name tag people read. (Screen: an id card: did:plc:…; a domain tag: alice.example.com.)

    Handles can change. The DID never does. Apps use the DID. People use the handle. (Screen: the handle swapped; the DID unchanged.)

    A handle points at a DID in one of two ways. A DNS text record, or a small file on the domain's website. (Screen: a DNS TXT record and a well-known file, each holding the DID.)

    And the DID points back at the handle. Both directions must agree, or anyone could claim a name for someone else's account. (Screen: the DID document pointing back at the handle.)

    Own a domain, and it can be your handle. The name is yours, not the app's. (Screen: your own domain as your handle.)

Unit 3: The DID document: a key, a handle, and an address


    Idea. Resolving a DID yields a document with the signing key, the handle claim and the PDS address; atproto allows did:web and did:plc.

    Why it matters. Everything that verifies or locates an account starts here.

    Context assumed. Unit 2.

    Metaphor. A page with three rows.

    Misconception. The DID document holds your data. Addressed at beat 4.

    Visual language. Document page, key, handle, address rows; two method chips.

    Target length. 40 to 50 seconds.

    Check question. Name the three things in a DID document that the protocol uses.

Script:

    Resolve a DID and you get a document. A page with three rows that matter. (Screen: a DID resolving to a document.)

    A signing key. Every change to your data is signed with its secret half. (Screen: the signing key.)

    The handle you claim. (Screen: the handle claim.)

    And the address of your host server, your personal data server. That is how anyone finds where your data lives today. (Screen: the PDS address.)

    Two kinds of DID are allowed. did web, which is just a domain you control. And did plc, which is next. (Screen: two methods: did:plc, did:web.)

Unit 4: did:plc: an identity you can recover


    Idea. A did:plc identity is a ledger of signed operations whose genesis hash is the DID; rotation keys above the signing key install a new one when it is lost; a central directory, not a blockchain, keeps the ledger; for three days a higher rotation key you kept yourself can undo a change. That is the recovery.

    Why it matters. It is why losing a key or a domain does not mean losing the account.

    Context assumed. Unit 3.

    Metaphor. A ledger of changes, with master keys above the everyday key.

    Misconception. did:plc is a blockchain. Addressed at beat 3.

    Visual language. did:plc card, ledger of changes, master keys above the signing key, directory with a blockchain crossed out, a paper key and a 72 hour window.

    Target length. 55 to 65 seconds.

    Check question. What can a rotation key you kept on paper do in the three days after a bad change?

Script:

    did plc is a DID method made for atproto. Your identity is a ledger of signed changes, and the DID is a hash of the first one. (Screen: a did:plc card; a ledger of signed operations beneath.)

    Rotation keys are the master keys, above the everyday signing key. Lose or leak the signing key, and a rotation key signs a change that installs a new one. (Screen: rotation keys above the signing key; a new signing key installed.)

    The ledger is kept by a central directory. Not a blockchain: a server that checks and stores signed operations. (Screen: a directory server holding all ledgers; a blockchain crossed out.)

    Keep one rotation key yourself, on paper. For three days after any change, a higher rotation key can undo it. That is the recovery. (Screen: a paper key above the server's key; a 72 hour window undoing a change.)

    Why not just use a domain? Because you might lose it. did plc needs no domain to keep your identity. (Screen: did:plc beside did:web: no domain needed.)

Unit 5: A repository is a signed set of records


    Idea. All of an account's public data is one repository: records in collections, a Merkle search tree reducing it to one root, every version signed with the DID's key, so anyone can verify and anyone can serve it.

    Why it matters. Self-authenticating data is what makes hosts interchangeable.

    Context assumed. Unit 3.

    Metaphor. A ledger box with a seal on the lid.

    Misconception. The server vouches for your data. Addressed at beat 5.

    Visual language. Ledger box, cards in collections, tree with a root, seal, a stranger's server.

    Target length. 45 to 55 seconds.

    Check question. What does the seal on a commit cover?

Script:

    All your public records live in one repository. A ledger box of records: posts, likes, follows, whatever an app defines. (Screen: a ledger box of cards.)

    Records are sorted into collections, one per record type. Each record has a key inside its collection. (Screen: cards sorted into collections.)

    Above the records sits a hash tree. One root hash stands for everything. (Screen: a tree above the cards with one root.)

    Every version of the repository is a commit: a seal on the root. Signed with the key in your DID document. (Screen: a seal on the root; the signing key.)

    So anyone can check it, and anyone can serve it. The repository proves itself. The server holding it does not matter. (Screen: the same box served from a stranger's server, still verified.)

Unit 6: Every record has an address


    Idea. An AT URI is DID, collection and record key; collections are reversed-domain NSIDs; most keys are TIDs that sort by time; some collections hold one record keyed self; resolution goes through the DID to the host.

    Why it matters. Addresses that survive a change of host are what portability needs.

    Context assumed. Unit 5.

    Metaphor. A street address with three parts.

    Misconception. Record URLs point at a server. Addressed at beat 5.

    Visual language. Monospace URL in three colors; reversed domain; TID; self; resolution arrows.

    Target length. 50 to 60 seconds.

    Check question. Which part of an AT URI says who defined the record type?

Script:

    A record's address is an AT URI: a street address with three parts. The DID. Then the collection. Then the record key. (Screen: at://did:plc:…/app.bsky.feed.post/3jzf….)

    The collection is a name with a domain in it, reversed. app dot bsky dot feed dot post. The domain says who defined it. (Screen: the collection name as reverse domain.)

    Most record keys are TIDs: thirteen characters made from a timestamp. They sort by time. (Screen: a TID: 13 characters from a timestamp.)

    Some collections hold one record, keyed self. Your profile is one. (Screen: a record keyed self.)

    Resolve the DID to find the host. Ask the host for the collection and key. The address works no matter which server holds the data. (Screen: the address resolved: DID to host, then the record.)

Unit 7: Records are hashed, and links are hashes


    Idea. Records are canonical DRISL-CBOR bytes hashed with SHA-256 into CIDs; references carry CIDs, so a link is a hash; a reply names its parent by URI and CID; the hashes chain to the root.

    Why it matters. Content addressing is what makes verification and diffs possible.

    Context assumed. Unit 5.

    Metaphor. A fingerprint of the bytes.

    Misconception. Every reference is content-addressed. Addressed at beat 3: inside the tree links are hashes; between records a reference is an address with the CID beside it.

    Visual language. Bytes, hash funnel, CID chip, reply card with URI and CID, tree up to root.

    Target length. 50 to 60 seconds.

    Check question. Why does a reply carry both the parent's URI and its CID?

Script:

    Inside, a record is CBOR: a compact binary form of JSON. A fixed, canonical encoding, so the same record always gives the same bytes. (Screen: a record encoded as bytes.)

    Those bytes are hashed with SHA two fifty six. The hash, wrapped as a CID, is the record's fingerprint: its content identifier. (Screen: the bytes hashed into a CID.)

    Inside the repository every link is a hash: the tree points at each record by its CID. Between records, a reference is an address, often with the CID beside it. (Screen: the tree pointing at records by CID; a like pointing at a post by address plus CID.)

    A reply names its parent twice. The address, to find it. The CID, to know it is unchanged. (Screen: a reply with the parent's URI and CID.)

    Hashes of hashes, up to the root. Change one byte anywhere, and the root changes. (Screen: the tree of records up to the root.)

Unit 8: A commit is a signed root with a clock


    Idea. A commit names the DID, the tree root, a TID revision that only increases, and a signature; because the tree is a Merkle search tree, a change touches only one path, so a commit's diff is a few blocks; no other server can forge it, but your own host holds the key.

    Why it matters. This is the unit of sync and the unit of trust.

    Context assumed. Unit 7.

    Metaphor. A sealed page with a page number that only goes up.

    Misconception. Each commit ships the whole repository. Addressed at beat 4.

    Visual language. Commit card with four fields, rev counter, tree with one path highlighted, block diff, signature.

    Target length. 50 to 60 seconds.

    Check question. What does the rev field let a stranger decide without trusting anyone?

Script:

    Each version of a repository is a commit: a sealed page. It names the account, the root of the tree, a revision, and a signature. (Screen: a commit card: did, data, rev, sig.)

    The revision is a TID. It only goes up. It is a logical clock: anyone can tell which commit is newer. (Screen: the rev as a TID that only goes up.)

    The tree is a Merkle search tree. Change one record, and only the path from it to the root changes. (Screen: the tree: change one record, the path to the root changes.)

    So the difference between two commits is a few blocks. That is what travels when your repository changes. (Screen: a diff: the changed blocks only.)

    And the commit is signed with the key in your DID document. No other server can forge a version of you. Your own host can: it holds the key. So you choose it, and you can leave it. (Screen: the commit signed; every other server crossed out; your own host holding the key.)

Unit 9: Lexicon: schemas with a domain in their name


    Idea. Lexicon is the schema language for records, API calls and streams; each schema has an NSID whose reversed domain says who owns it; schemas are published as records; any app can read any other app's records.

    Why it matters. Shared, owned schemas are how independent apps interoperate on one network.

    Context assumed. Unit 6.

    Metaphor. A dictionary where each entry is owned by a domain.

    Misconception. Schemas are registered with Bluesky. Addressed at beat 2 and 4.

    Visual language. Schema card, reversed-domain name, four type chips, schema as a record, two apps reading one record.

    Target length. 50 to 60 seconds.

    Check question. How does a developer prove they own a schema name?

Script:

    What is a post? A like? A recipe? A Lexicon says. Lexicon is the dictionary: the schema language for records, API calls, and streams. (Screen: a schema card named com.example.recipe.)

    Every schema has a namespaced ID. Reverse the domain, add a name. Whoever controls the domain controls the schema. (Screen: the name reversed into a domain.)

    A schema defines a record type, a query, a procedure, a subscription, or a bundle of permissions. Typed fields, references, blobs, unions. (Screen: five kinds: record, query, procedure, subscription, permissions.)

    Schemas are published as records, in a repository. A DNS record on the domain says which DID holds them. (Screen: the schema published as a record in a repository.)

    So any app can read any other app's records, and know what they mean. The schema is public, named, and owned. (Screen: two apps reading the same recipe records.)

Unit 10: XRPC: every API call is a Lexicon method over HTTP


    Idea. API calls are HTTP at /xrpc/ plus the method's NSID; queries are GET and procedures are POST; the Lexicon types inputs and outputs; the atproto-proxy header asks the PDS to forward a call with a token it signs; one method name works on every server.

    Why it matters. It keeps the network one network without a central API.

    Context assumed. Unit 9.

    Metaphor. One name, every server.

    Misconception. Apps talk to a central API. Addressed at beat 4.

    Visual language. Request line, GET and POST chips, lexicon card, PDS forwarding, many servers.

    Target length. 50 to 60 seconds.

    Check question. What does the PDS add when it forwards a proxied request?

Script:

    Atproto's API is HTTP with one convention. The path is slash xrpc slash the method's namespaced ID. (Screen: GET /xrpc/com.atproto.repo.listRecords.)

    A query is a GET. Read only, cacheable. A procedure is a POST. It can change things. (Screen: GET for queries; POST for procedures.)

    The Lexicon describes the parameters, the body, and the response. So clients are typed, and inputs are checked. (Screen: the lexicon describing inputs and outputs.)

    You talk to your own host. A header can ask it to forward the call to another service. The host signs the forwarded request on your behalf. (Screen: a request to the PDS forwarded to another service.)

    One name, every server. That is what makes the network one network. (Screen: one method name, many servers.)

Unit 11: The PDS is your home: it holds your repo and your keys


    Idea. The personal data server stores the repository, the signing keys and usually a rotation key, answers the user's app, proxies to services, signs and publishes commits, and can be self-hosted under your own domain.

    Why it matters. The PDS is the one server you must trust, and you can be it.

    Context assumed. Units 8 and 10.

    Metaphor. A home in the cloud.

    Misconception. A host is a walled garden. Addressed at beat 5.

    Visual language. House with server, repo and keys inside, arrows to client and services, stream out, many houses.

    Target length. 40 to 50 seconds.

    Check question. Which two things does a PDS always store?

Script:

    Your personal data server is your home in the cloud. It stores your repository and holds your signing keys. Usually a rotation key too, unless you keep your own. (Screen: a house with a server inside: repo, keys.)

    It answers your app's requests, and forwards the ones meant for other services. (Screen: the PDS answering clients and forwarding to services.)

    Every change you make, it signs and publishes as a commit. (Screen: the PDS publishing commits to the stream.)

    You can run one yourself, on a small server, under your own domain. It joins the same network as everyone else. (Screen: a small server at home, federated with the rest.)

    A host is a home, not a walled garden. Thousands of hosts. One network. (Screen: many hosts, one network.)

Unit 12: Blobs ride beside the repository


    Idea. Media are blobs stored beside the repository: uploaded first into temporary storage, then referenced from a record by CID, type and size; unreferenced blobs are garbage collected; quotas are the host's, accepted types and sizes the schema's.

    Why it matters. It explains why media behaves differently from records in sync and migration.

    Context assumed. Unit 11.

    Metaphor. A parcel on a temporary shelf until a record claims it.

    Misconception. Images are inside the repository. Addressed at beat 1.

    Visual language. Parcel beside the ledger box, temporary shelf, record referencing the parcel, sweep.

    Target length. 45 to 55 seconds.

    Check question. When does a host start keeping an uploaded blob for good?

Script:

    Images, video, audio: blobs. Parcels that live beside the repository, not inside it. (Screen: a parcel next to the ledger box.)

    You upload a blob to your host first. It waits on a temporary shelf, not yet visible to anyone. (Screen: upload, then a temporary shelf.)

    Then a record references it: the blob's CID, its type, and its size. Now the host keeps it, and serves it. (Screen: a record referencing the blob by CID, type, size.)

    A blob nothing references is deleted after a while. Delete the last record that points at it, and the blob goes too. (Screen: an unreferenced blob swept away.)

    Quotas are the host's call. Which types and sizes a record may carry is the schema's call, checked when the record is written. (Screen: a quota set by the host; accepted types and max size set by the schema.)

Unit 13: The firehose: every change, in order, signed


    Idea. Each host publishes an event stream: commits with the changed blocks, identity and account events, each with a sequence number for resumption, as binary frames over a WebSocket.

    Why it matters. It is how the rest of the network learns anything.

    Context assumed. Units 8 and 11.

    Metaphor. A pipe of numbered cards.

    Misconception. The firehose is a feed of posts. Addressed at beat 2 (commits and blocks).

    Visual language. Pipe from a host, commit event with blocks, identity and account event chips, sequence numbers, cursor, binary frames.

    Target length. 45 to 55 seconds.

    Check question. What does a consumer send when it reconnects?

Script:

    Every host publishes a stream of events. Atproto calls it the firehose. (Screen: a pipe of events leaving a host.)

    The main event is a commit: the signed root, and the blocks that changed. Enough to verify it against the version before. (Screen: a commit event carrying the changed blocks.)

    Other events say your identity may have changed, or that an account was deactivated, or taken down. (Screen: identity and account events.)

    Every event has a sequence number: a pipe of numbered cards. Disconnect, come back with your last number, the cursor, and you get what you missed. (Screen: sequence numbers on each event; a cursor.)

    It is a websocket carrying binary frames. Built for servers, not browsers. Lighter forms come later. (Screen: binary frames over a websocket.)

Unit 14: A relay is a crawler that re-broadcasts


    Idea. A relay subscribes to many hosts and merges their streams into one, verifying commits against the previous tree state; since sync v1.1 relays are non-archival; anyone can run one, full-network or partial; a relay is a funnel, not a gate.

    Why it matters. It is the scaling trick, and it is not a point of control.

    Context assumed. Unit 13.

    Metaphor. A funnel.

    Misconception. The relay owns the network. Addressed at beat 5.

    Visual language. Many pipes into one, verification check, no archive, second relay, partial relay, funnel.

    Target length. 55 to 65 seconds.

    Check question. What does a non-archival relay keep, and what does it not?

Script:

    Nobody wants to subscribe to thousands of hosts. A relay subscribes to all of them and merges the streams into one. (Screen: many host pipes joining one big pipe.)

    It checks each commit as it passes: the signature, and that the changed blocks fit the tree. Commits that fail are logged today, and will be dropped once strict checking is switched on. (Screen: a commit checked against the previous version.)

    Since sync version one point one, a relay keeps no archive. It verifies and forwards. That makes it cheap in storage. The cost is bandwidth. (Screen: no archive behind the relay; a bandwidth meter.)

    Anyone can run one. Blacksky runs a full network relay. A relay can be full network, or cover just one community. (Screen: a second relay run by someone else; a small partial relay.)

    A relay is a funnel, not a gate. Your data is still yours, on your host, whatever the relay does. (Screen: the relay as a funnel, not a gate.)

Unit 15: An AppView is a materialized view


    Idea. An app indexes the stream into its own database, the AppView, computing counts, feeds and search; two apps can build two views; new apps index records that predate them; write to the PDS, read from the AppView.

    Why it matters. It is why there is a global view without a central database.

    Context assumed. Unit 14.

    Metaphor. A cached query over a stream.

    Misconception. Apps query hosts directly. Addressed at beat 1.

    Visual language. Stream into index, counts and search chips, two indexes over one stream, old records into a new index, the write-read loop.

    Target length. 55 to 65 seconds.

    Check question. Why is it called a view?

Script:

    An app does not query hosts one by one. It drinks from the stream and builds its own index. That index is the AppView: a cached query over the stream. (Screen: the big stream flowing into an index.)

    Like counts. Follower counts. Search. Timelines. All computed from public records, by the app, for the app. (Screen: counts, feeds, search built from records.)

    Two apps can build two views of the same network. It is called a view because it is just one way of looking. (Screen: two different AppViews over the same stream.)

    A new app can index records that existed before it did. The data was never locked to the first app that read it. (Screen: a new app reading old records.)

    So the loop is: write to your host, it flows through the stream, the view indexes it, the app reads the view. (Screen: write to the PDS; read from the AppView.)

Unit 16: Jetstream and Tap: lighter ways to drink


    Idea. Jetstream turns the firehose into filtered JSON without signatures, and since version two can replay the past, so you trust its operator; Tap keeps verification and fetches a repository's past from the host before live events; choose by how much you must verify.

    Why it matters. Most developers never touch the raw firehose.

    Context assumed. Unit 13.

    Metaphor. Three taps on one pipe.

    Misconception. Jetstream is the firehose. Addressed at beat 2.

    Visual language. Heavy pipe, JSON tap with filter, Tap with backfill then live, bot versus archive, three stacked.

    Target length. 50 to 60 seconds.

    Check question. What does Jetstream drop that the firehose carries?

Script:

    The raw firehose is heavy: binary blocks, signatures, thousands of events a second. (Screen: the full firehose: binary, signed, heavy.)

    Jetstream turns it into plain JSON, lets you filter by collection, and since version two can replay the past. It drops the signatures, so you trust the Jetstream you connect to. (Screen: Jetstream: JSON events, filtered by collection, replay; signatures dropped.)

    Tap goes the other way: it keeps the verification. Track a repository, and Tap fetches its past from the host, checks every commit, then hands you the live events. (Screen: Tap: the past from the host, every commit checked, then live.)

    A bot or a prototype can live on Jetstream. Archives, moderation, and anything that must prove authorship need the signed stream. (Screen: a bot on Jetstream; an archive on the firehose.)

    Three taps on one pipe. Same events, three ways to receive them. Pick by how much you need to verify. (Screen: the three stacked: firehose, Tap, Jetstream.)

Unit 17: A feed is a service, not a setting


    Idea. In Bluesky, a custom feed is a service with its own DID that answers with a skeleton of post URIs; the app's server hydrates them; the feed is declared by a record in its author's repository; any HTTPS server can host one.

    Why it matters. Algorithmic choice is a protocol feature, not an app setting.

    Context assumed. Unit 15.

    Metaphor. A skeleton the app puts flesh on.

    Misconception. The feed service returns posts. Addressed at beat 2.

    Visual language. Feed service with DID, skeleton list, AppView filling it, declaration record, home server.

    Target length. 50 to 60 seconds.

    Check question. What does a feed generator return, and what does it not?

Script:

    In Bluesky, a custom feed is a service with its own DID. (Screen: a feed generator with its own DID.)

    Ask it for a feed, and it answers with a skeleton: a list of post addresses, in order. No post content. Just which ones. (Screen: the answer: a list of post addresses.)

    The app's server fills the skeleton in with the posts, the authors, the counts. (Screen: the app's server filling in the posts.)

    To publish a feed, you write one record in your own repository that points at the service. Anyone can find it, pin it, and share it. (Screen: a record in your repo pointing at the feed.)

    The ranking is the feed's business. The data is everyone's. A feed can run from any server that answers HTTPS. (Screen: a feed run from a small server.)

Unit 18: Labels: moderation as a subscribable layer


    Idea. A label is a source, a subject and a value, signed by a labeler's key; users choose labelers and the app requests their labels; what a label does is set outside the label (the labeler suggests, the user adjusts, the app enforces); takedowns at the host are a separate lever.

    Why it matters. It separates speech from reach and lets moderation be composed.

    Context assumed. Unit 15.

    Metaphor. A sticker from a labeler you chose to listen to.

    Misconception. Labels delete content. Addressed at beat 4 and 5.

    Visual language. Sticker with three fields, labeler key, subscription, hide/warn/blur chips, takedown at the host.

    Target length. 50 to 60 seconds.

    Check question. Who sets what a label does to a post?

Script:

    A label is a sticker on content or an account. Who put it there, what it is on, and a short value: spam, nudity, whatever the service that issued it defines. (Screen: a sticker on a post: source, subject, value.)

    Labels are signed by a labeler: a service with its own DID and key. (Screen: a labeler service signing labels.)

    You choose which labelers to listen to. Your app asks for their labels by name on every request. (Screen: a user subscribing to two labelers.)

    What a label does, hide, warn, blur, is set outside the label: the labeler suggests, the user adjusts, the app enforces. Speech and reach are two layers. (Screen: labels applied: hide, warn, blur; labeler, user, app.)

    A host can still take content down outright. That is a different lever, at a different layer. (Screen: a takedown at the host, separate from labels.)

Unit 19: OAuth: apps log in without your password


    Idea. OAuth: an app is identified by a public metadata URL with no secret; login happens at the PDS or its entryway, the authorization server; PAR, PKCE and DPoP are required so tokens are bound to a per-session key; app passwords are the old way.

    Why it matters. Third-party apps are the point of an open network, and this is how they are safe.

    Context assumed. Unit 11.

    Metaphor. A key cut for one lock.

    Misconception. Apps register with a central authority. Addressed at beat 1.

    Visual language. App with URL, login at the house, three locks, bound token, crossed app password.

    Target length. 45 to 55 seconds.

    Check question. What binds an access token to one app instance?

Script:

    This is OAuth, the web's login handshake. An app identifies itself with a URL: a public metadata document. No registration form, no shared secret. (Screen: an app with a metadata URL instead of a secret.)

    You log in at your host, or at the login service your host names. Never inside the app. That host or service is the authorization server. (Screen: the login happens at the PDS or its entryway.)

    Three protections are mandatory. PAR, a pushed request. PKCE, a code challenge. And DPoP: tokens bound to a key this copy of the app made for this session. (Screen: three locks: PAR, PKCE, DPoP.)

    A stolen token is useless somewhere else. It is a key cut for one lock: bound to the key that asked. (Screen: a token that only works from that app instance.)

    App passwords are the old way: a second password, nearly full access. OAuth replaces them with scopes: named permissions, next. (Screen: an app password, crossed out as the old way.)

Unit 20: Permissions name a collection, not an account


    Idea. A scope names a resource and the thing it covers: repo plus a collection, rpc, blob, account, identity; a namespace owner can bundle its record and service scopes into a permission set, published as a Lexicon; the user sees the list; resources are abstract, not endpoints.

    Why it matters. Least privilege at the level of record types.

    Context assumed. Unit 19.

    Metaphor. A key labeled with one drawer.

    Misconception. OAuth grants all-or-nothing. Addressed at beat 1.

    Visual language. Scope chip, five resource chips, permission set card, approval list, shape chip.

    Target length. 45 to 55 seconds.

    Check question. What does repo:com.example.recipe allow?

Script:

    A scope says what an app may touch: a key labeled with one drawer. Repo, then a collection: this app may write recipes. Nothing else. (Screen: a scope string: repo:com.example.recipe.)

    Other scopes cover calls to a service, uploading blobs, account details, and identity. (Screen: more scopes: rpc, blob, account, identity.)

    Whoever owns a schema namespace can bundle its record and service scopes into a permission set, published as a Lexicon. Named and owned, like any schema. (Screen: a permission set as a lexicon.)

    You see the list before you say yes. (Screen: the user sees the list before approving.)

    Resources are described in the abstract, not tied to particular endpoints. The shape is fixed: name the thing, not the account. (Screen: the scope shape: resource, then the thing it names.)

Unit 21: Moving house without asking permission


    Idea. Migration exports the repository as a CAR and the blobs, imports them at a new host with new keys, updates the DID document, activates the new account and deactivates the old; if the old host is gone, a rotation key you kept yourself still lets you move.

    Why it matters. This is the founding promise delivered.

    Context assumed. Units 4 and 11.

    Metaphor. Moving house with your own boxes.

    Misconception. You need the old host's cooperation. Addressed at beats 1 and 5.

    Visual language. User leaving a house, CAR file and parcels, new house with new keys, DID document updated, old house dark.

    Target length. 50 to 60 seconds.

    Check question. Which change makes the network treat the new host as yours?

Script:

    The goal was always that you can move house, boxes and all, without the old host's help. (Screen: a user leaving one host for another.)

    Export your repository as one file, and your blobs. Anyone can request them; the data is public and signed. (Screen: export the repo and blobs.)

    Import them at the new host. It gives you new signing keys. (Screen: import at the new host; new keys.)

    Update your DID document: new host, new key. The DID itself does not change. Your followers notice nothing. (Screen: the DID document updated to the new address.)

    Activate the new account, deactivate the old. The network sees the events and follows you home. If the old host is gone, a rotation key you kept yourself still lets you move. (Screen: the new account activated, the old deactivated; a paper rotation key.)

Unit 22: Public by default; Spaces is the private extension


    Idea. Everything in a repository is public by design; Spaces is an alpha extension where a space is a gated mini-network with per-space permissioned repos on the author's PDS; it gives access control, not confidentiality; the core is settling and a launch is expected in 2026.

    Why it matters. The series must be honest about the one thing atproto does not yet do.

    Context assumed. Units 11 and 14.

    Metaphor. A gated miniature network.

    Misconception. Spaces encrypt your data. Addressed at beat 4.

    Visual language. Public repo, gated mini-network, per-space repo on the house, lock versus key, alpha tag.

    Target length. 55 to 65 seconds.

    Check question. Who can read a record in a space?

Script:

    Everything in a repository is public. That is a design choice, and it is what makes the big shared stream possible. (Screen: a repo with every record visible.)

    For non public data there is an extension: atproto Spaces. A space is a miniature network, gated so only certain people and apps can read it. (Screen: a gated miniature network.)

    Records in a space live in a separate, permissioned repository on the author's host. A space authority, a DID, decides who gets in. (Screen: a per-space repo on the author's PDS.)

    Spaces give access control, not confidentiality. Data in a space is not encrypted. Anyone admitted can read it. (Screen: access control, not encryption.)

    The alpha opened in August twenty twenty six. Breaking changes have landed since; the core is settling, and a launch is expected this year. (Screen: alpha, August 2026; core settling; launch expected this year.)

Unit 23: Who keeps the copy: non-archival relays and Hubble


    Idea. Relays no longer keep repositories, so a dead host loses its data unless you kept a backup; Hubble is a public mirror of every repository, funded by a Bluesky grant, with no media and deletions respected, so users can recover and apps can backfill.

    Why it matters. Portability needs durability, and the protocol itself does not provide it.

    Context assumed. Unit 14.

    Metaphor. A public library copy.

    Misconception. The network keeps your data if your host dies. Addressed at beat 2.

    Visual language. Relay without archive, dark host, Hubble mirror, no-media chip, deletion respected, user recovering.

    Target length. 45 to 55 seconds.

    Check question. What does Hubble store, and what does it not?

Script:

    Relays no longer keep a copy of every repository. (Screen: a relay with no archive behind it.)

    So if a host goes dark, its repositories go with it. Apps cannot load the past. Users without their own backup cannot recover. (Screen: a host goes dark; its data with it.)

    Hubble is a public mirror of every repository, a library copy of the whole network, synchronised live. Built and run by one developer's project, with a grant from Bluesky. (Screen: Hubble: a public mirror of every repository.)

    It stores records, not media. It respects deletions. (Screen: no media; deletions respected.)

    Lose your host, and your repository is still there to recover. The protocol made the data portable. A mirror makes it durable. (Screen: a user recovering from the mirror.)

Unit 24: Where atproto stands


    Idea. In 2026 an IETF working group was chartered for the repository format, sync, the AT URI and identifier resolution; identity has a neutral steward, founded in 2026, and replicas; sync v1.1 runs on the relays with Tap as reference consumer and pieces to come; Spaces is the open front, with a launch expected this year.

    Why it matters. It tells the learner what is settled and what is moving.

    Context assumed. All.

    Metaphor. A map with the surveyed and unsurveyed parts.

    Misconception. The protocol is finished. Addressed at beat 4.

    Visual language. IETF chip with four scope items, PLC chip, sync chips, Spaces chip, the four blocks from unit 1.

    Target length. 45 to 55 seconds.

    Check question. Which four things is the IETF group standardizing first?

Script:

    In twenty twenty six an IETF working group was chartered for the protocol. Its first scope: the repository format, the sync mechanism, the AT URI, and identifier resolution. (Screen: IETF working group, chartered 2026.)

    Identity has a neutral steward, the PLC Organization, founded this year, and the directory can be replicated by anyone. (Screen: PLC Organization; read replicas.)

    Sync one point one runs on the relays, with Tap as its reference consumer, and a few pieces still to come. Relays are non archival; Hubble keeps the copy. (Screen: sync 1.1 on relays; Tap; non-archival relays; Hubble.)

    The open front is non public data: Spaces is in alpha, and a launch is expected this year. (Screen: Spaces alpha; permissioned data as the focus.)

    A map with surveyed and unsurveyed parts. Identity, repositories, sync: settled enough to standardize. Private data: still being designed. (Screen: four blocks: identity, repos, sync, apps.)

Review notes

The independent review (AT Protocol Curriculum Review) returned 24 accuracy findings, 10 structure findings, 5 source findings and 5 other notes against revision 1. What changed:

    Wrong claims fixed. The PLC Organization was founded in early 2026, so units 4 and 24 no longer say it "is being set up" (A1); unit 8 now says no other server can forge you, because your own host holds the signing key (A2); unit 12 now says quotas are the host's but accepted types and sizes are the schema's, checked when the record is written (A5).

    Stale facts brought to October 2026. Jetstream v2 replays the past, so Tap's distinction is verification, not history (A6); "thousands of events a second" (A7); Spaces' core is settling with a launch expected this year (A18, A19); sync v1.1 runs on the relays with pieces still to come (A8); failed commits are logged today and will be dropped once strict checking is on (A9); a relay is cheap in storage, the cost is bandwidth (A10).

    Models completed. Unit 4 now teaches the recovery window and the paper rotation key, says the directory is not a blockchain, and moves governance to unit 24 (A4, S2, S3); unit 7 separates links inside the tree from references between records (A3); unit 21 activates the new account and names the rotation key you kept (A16, A17); unit 11 says the host usually holds a rotation key too (O2); unit 9 names the fifth Lexicon kind (A23); unit 13's identity event says only that something may have changed (A22); unit 5 says records, not data (A24); unit 23 says users without a backup cannot recover (A20).

    Overstatements trimmed. Label behaviour is set outside the label: labeler, user, app (A11); the authorization server is the host or its entryway (A12); the DPoP key is per session (A13); "no registration form", "app passwords are the old way" (A14); permission sets belong to a namespace (A15); unit 17 says the app's server hydrates and that feeds are a Bluesky feature (A21, S8).

    Structure. All fifteen unspoken metaphors are now spoken on the beat whose screen draws them, or the card changed to match (S1); unit 5 says "commit"; unit 19 names PAR, PKCE and DPoP; "DID" is expanded in unit 2, OAuth named in unit 19, "backfill" replaced by "load the past" (S4, S5); unit 17's screen no longer shows a signed request (S10); idea-map dependencies completed (S7).

    Sources. Fourteen new recorded statements, an ellipsis in the Spaces quote (Q1), and a note that the AT Stack page's sentence "Relays handle all of your events, like retrieving large-scale metrics" describes AppViews, not relays, and is not quoted here (Q5).

    Carried to the posts, not the videos. Commit version and prev fields (O1); where a new app fetches old records now that relays are non-archival (O3); the inter-service token's name (O4); group hosts in the Spaces series (O5).

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime