I’d also like to voice a strong dissenting opinion: a separate URI scheme follows long-standing precedent, reduces cognitive overhead and software complexity by making the transport distinction explicit, and has less ambiguous error handling. I think this direction trades short-term convenience for long-term ambiguity.
The transport models are incompatible, not variants of each other
Public atproto records flow through a well-understood pipeline: repos export as CAR files, relays consume firehose events, AppViews index everything, and any party can fetch any record without credentials. The public transport supports and encourages auditing, with visible ties between identity and cryptographic verification.
Permissioned data spaces introduce authorization checks, access-controlled fetching, and records that deliberately never appear in the public sync pipeline. The permissioned transport encourages and supports deniability, putting up barriers to proving that an identity was the historical owner of a piece of content.
The creation and usage flows for public and permissioned data require two different transports with different security guarantees. The mechanisms that make at:// resolution work today simply don’t apply, and no amount of shared syntax changes that.
Resolution paths genuinely differ
One argument for unification is that at:// doesn’t specify a sync mechanism, so the scheme isn’t making transport promises in the first place. I don’t think that holds up. In practice, every existing consumer of an at:// URI resolves it the same way: resolve the identity, find the PDS, call com.atproto.repo.getRecord or pull from sync.
A permissioned record can’t be resolved that way; it requires an authenticated flow with different endpoints and different failure modes. Resolution and access are tightly coupled. When two identifiers require different resolution procedures, the identifier itself is the right place to signal that. Otherwise every resolver has to attempt one path, fail, and guess.
Web precedent overwhelmingly favors distinct schemes here
When other protocols dealt with this exact same scenario (one resource, multiple transport or security contexts), they consistently landed on separate schemes. HTTP and HTTPS address the same resources, but the security context differs, and that alone justified a distinct scheme. WebSockets made the same call with ws:// and wss://. And IPFS separated ipfs:// from ipns:// because immutable content addressing and mutable name resolution resolve differently, even though both identifiers live side by side in one ecosystem. In every case, the payoff is legibility: the identifier itself tells you which transport you’re dealing with before you ever touch the network.
The standards point the same way. RFC 3986 frames a scheme as identifying how to interact with a resource, not just what the resource is, and RFC 7595’s registration guidance treats a differing access method as the case that warrants a new scheme. Distinct schemes would give atproto that same legibility, with public and permissioned transports readable at a glance to resolvers, link handlers, and the humans looking at a URI. That pattern has held for decades: when transport or security semantics diverge, the scheme diverges. Unification would be a highly unusual choice here.
Error semantics become ambiguous
Under a unified scheme, what does a failed resolution mean? With public at://, a not-found unambiguously means the record doesn’t exist. With permissioned data in the same namespace, a failure could mean the record doesn’t exist, or it exists and you lack access, or you queried the wrong transport entirely. The failure itself becomes ambiguous because the two transports have very different success and failure scenarios, and different recovery steps when something goes wrong.
Distinct schemes keep the error model of each transport clean: public resolution failures stay unambiguous, and permissioned resolution can define its own access-denied semantics without contaminating the public contract.
Keep them separate
My general opinion is that these differences are enough to warrant separate and distinct protocol identifiers. Permissioned data needs its own URI format, its own way to resolve where data is stored, and its own mechanism to prove access to that data. Trying to merge it in would create unnecessary complexity and strain for the development ecosystem and the spec.
A distinct scheme costs a small amount of aesthetic unity now. What it buys is permanent: unambiguous resolution, safer defaults for the existing ecosystem, and cleaner failure modes. I’d rather we make that trade deliberately than find out the hard way, through leaked URIs and confused resolvers, after deployment.