Creating a did:web ATProto Identity: A Proof of Concept

Last week, I found myself going down yet another technical rabbit hole—this time exploring the did:web method for ATProto identities. What started as casual curiosity about decentralised identity standards ended up with me creating @web.ewancroft.uk , a functional proof of concept that demonstrates how web-based DIDs can work within the ATProto ecosystem.

Disclaimer: This experiment was purely for research and educational purposes. I have no plans to switch my main ATProto identity to did:web and will continue using did:plc as my primary method.

The Background: Why did:web Caught My Interest

Most ATProto accounts, including my main one, use the did:plc method—Bluesky's Public Ledger of Credentials system. It's convenient, works well, and handles the complexities of decentralised identity without much fuss. The PLC directory maintains a cryptographically verifiable record of your identity, and for most users, it's perfectly adequate.

But there's something appealing about the independence that did:web offers. Your identity document lives on your own domain at /.well-known/did.json, which means you're not dependent on any particular organisation's infrastructure. It's the difference between renting a flat and owning your own house—both can work perfectly well, but ownership gives you a different kind of control.

The did:web method is elegantly simple: your DID is literally just did:web:yourdomain.com, and anyone can resolve it by fetching https://yourdomain.com/.well-known/did.json. No special infrastructure required beyond standard web hosting. The trade-off is obvious: you need to maintain your own domain and hosting. But for someone who already runs their own website (like myself), this isn't much of a burden.

I've been thinking about this concept for a while, particularly after writing about the case for multiple methods where I explored the theoretical benefits of supporting both did:plc and did:web simultaneously. This seemed like the perfect opportunity to test the waters with a practical implementation.

Enter the Tool: Luke's Brilliant Solution

Rather than wrestle with the technical implementation myself—which would have involved manually constructing JWT tokens, handling secp256k1 cryptography, and navigating the various ATProto endpoints—I discovered atproto-did-web.lukeacl.com , created by @lukeacl.com .

This tool transforms what could be a complex, error-prone process into a straightforward web flow. What really impressed me was how Luke managed to abstract away all the cryptographic complexity while still being completely transparent about what's happening at each step.

Looking at the source code , it's a well-designed SolidJS application that demonstrates excellent software engineering principles. The interface is clean, the error handling is robust, and the step-by-step approach prevents the sort of mistakes that would leave you with a half-configured identity.

Under the Hood: Technical Implementation Details

The tool's architecture is particularly clever. Built with SolidJS and styled with Tailwind, it creates a responsive, single-page application that guides users through each phase of the setup process. The state management is handled elegantly with SolidJS signals, maintaining all the necessary data throughout the multi-step workflow.

What's especially noteworthy is the security approach. All cryptographic operations happen client-side using the @atproto/crypto library. Your private key is generated (or imported) locally, never transmitted anywhere, and used only for the specific JWT signing operations required by the ATProto specification.

The JWT construction is particularly interesting. The tool creates a properly formatted authentication token with the com.atproto.server.createAccount lexicon method, signed with your private key to prove control over the DID. This is exactly how the ATProto specification expects authentication to work for did:web accounts.

const payload = JSON.stringify({
lxm: "com.atproto.server.createAccount",
iss: `did:web:${domain()}`,
aud: `did:web:${new URL(pdsEndpoint()).hostname}`,
exp: Math.floor(Date.now() / 1000) + 180,
});

The tool also includes thoughtful touches like allowing users to skip DID file validation (useful for testing) and providing detailed error messages when things go wrong. There's even a hidden debug mode that displays all the internal state if you click on the page title—a nice touch for developers who want to understand what's happening behind the scenes.

The Process, Step by Step

The tool breaks the setup into seven manageable phases, each building on the previous one:

1. Introduction and Orientation

The tool starts with a clear explanation of what's about to happen. All operations are performed client-side, with only necessary network calls made to validate DID documents and communicate with your PDS. No private data is stored or shared, which is exactly what you want for this sort of sensitive operation.

2. Private Key Management

You can either generate a fresh secp256k1 key pair or import an existing one. The tool uses the @atproto/crypto library to handle this properly, generating cryptographically secure keys that conform to the ATProto specification. Once you have a private key, the tool derives the corresponding public key and DID identifier.

The interface handles this elegantly—if the private key field is empty, you see a "Generate" button. Once you've got a key, the button changes to "Next" for validation. Simple, but effective design.

3. Identity Configuration

Here's where you specify the fundamental details: your PDS endpoint, the domain where your DID document will live, and your desired handle. The tool validates that these are properly formatted and then constructs the initial DID document structure.

The domain field is particularly important—this needs to be a domain you actually control, since you'll need to upload files to it. The handle can be either on your own domain or on your PDS's domain, depending on your preferences and setup.

4. Initial DID Document Creation and Validation

The tool generates your first DID document, which includes your public key as a verification method and your PDS as a service endpoint. The document follows the W3C DID specification perfectly, with the proper @context declarations and multikey encoding for the public key.

What I appreciated here is that the tool shows you exactly what needs to be uploaded and where. You get a direct link to click and verify that your DID document is accessible at the expected URL. No guesswork involved.

The validation step fetches your uploaded DID document and compares it byte-for-byte with what the tool expects. If something's wrong, you get a clear error message rather than cryptic failures later in the process.

5. Account Creation

This is where things get properly technical. The tool constructs a JWT token signed with your private key, proving to the PDS that you control the DID you're claiming. The account creation request includes your email, a temporary password, and optionally an invite code if your PDS requires one.

For custom domain handles, there's an additional step where you need to create a DNS TXT record. The tool provides the exact host and value you need:

Host: _atproto.your.handle.domain
Value: did=did:web:yourdomain.com

This DNS record allows the ATProto system to verify that you control both the domain hosting your DID document and the domain used in your handle. It's a neat way of ensuring authenticity without requiring complex infrastructure.

6. Updated DID Document with PDS-Generated Keys

Once your account is created, the PDS generates additional cryptographic material that needs to be reflected in your DID document. The tool automatically fetches these updated verification methods and constructs a new DID document for you to upload.

This step caught me off guard initially—I expected the DID document creation to be a one-time thing. But it makes sense from a security perspective. The PDS needs to establish its own cryptographic relationship with your identity, which requires updating the DID document with the new verification methods.

7. Account Activation

The final step activates your account for use within the ATProto network. At this point, your did:web identity is fully functional and ready to interact with other ATProto services.

What I Learned: Technical Insights

Working through this process revealed several interesting aspects of how did:web fits into the ATProto ecosystem:

Cryptographic Complexity Made Simple: The tool handles secp256k1 key generation, JWT signing, and multikey encoding without requiring users to understand the underlying mathematics. This is exactly how complex cryptography should be presented—invisible when it works, clear when it doesn't.

The Importance of Proper Error Handling: Luke's implementation includes comprehensive error checking at each step. Invalid private keys get caught early, network failures provide meaningful messages, and validation steps prevent you from proceeding with incorrect configurations.

Client-Side Security: Everything sensitive happens in your browser. The tool never transmits your private key or stores any personal data on external servers. This is crucial for a tool dealing with identity creation and cryptographic material.

Standards Compliance: The generated DID documents conform perfectly to the W3C DID specification and ATProto requirements. The tool doesn't take shortcuts or make assumptions about what might work—it follows the standards precisely.

Integration with Existing Infrastructure: Despite being an alternative identity method, did:web integrates seamlessly with existing ATProto services. Once created, the account works identically to any did:plc account from the user's perspective.

The Current Reality: Limitations and Workarounds

While the process works beautifully, it does highlight some current limitations of the ATProto ecosystem regarding did:web support:

Single Method Constraint: You can't currently have both did:plc and did:web methods on the same identity. You have to choose one or the other, which means maintaining separate accounts if you want to experiment with both approaches.

PDS Compatibility: Not all PDS implementations may support did:web accounts perfectly. The mainstream Bluesky PDS works fine, but smaller or experimental PDS instances might have quirks.

DNS Dependency: For custom domain handles, you're now dependent on DNS resolution working correctly. If your DNS provider has issues, it could affect your ability to authenticate, even if your PDS is working fine.

Backup Complexity: Backing up a did:web identity requires preserving both your private key and your domain hosting setup. With did:plc, the cryptographic material is sufficient for recovery.

Why This Matters

Creating this proof of concept reinforced something I've been thinking about regarding digital identity and platform independence. While did:plc is perfectly functional for most users, having did:web as an option provides genuine choice about how you manage your online presence.

Ideally, I'd love to see both did:plc and did:web coexisting on the same DID document—having the convenience of the PLC system alongside the independence of web-based identity resolution. That would create a proper safety net: if Bluesky's infrastructure becomes unavailable, applications could fall back to the did:web resolution. If your domain hosting has issues, the did:plc method would still function perfectly. But that's not how things work right now, unfortunately.

For the moment, it's about having alternatives rather than true redundancy. The flexibility is what matters, even if we can't quite achieve the ideal hybrid approach yet.

The Broader Picture: Decentralisation in Practice

What excites me about tools like Luke's is how they make advanced concepts accessible. Decentralised identity shouldn't require a computer science degree to implement. When the barrier to entry is low enough, more people experiment, and more experimentation leads to better understanding of what works and what doesn't.

The did:web method represents a fascinating middle ground between traditional web infrastructure and cutting-edge decentralised protocols. It leverages existing web standards (HTTPS, DNS, JSON) while providing cryptographic verifiability and interoperability with modern decentralised systems.

This approach also demonstrates how the ATProto ecosystem can accommodate different trust models and infrastructure preferences. Some users want the convenience of managed infrastructure (did:plc), others prefer full control over their identity hosting (did:web), and some might want hybrid approaches that combine multiple methods. The protocol's flexibility in supporting these different models is genuinely impressive.

Looking Forward: Potential Improvements

While Luke's tool works brilliantly for its intended purpose, there are some areas where future versions could be even more powerful:

Key Management Integration: Supporting hardware security keys or integration with existing key management systems could make the process more secure for enterprise users or those with existing cryptographic infrastructure.

Bulk Operations: For organisations wanting to create multiple did:web identities, batch processing capabilities could be valuable.

Migration Tools: Helping users migrate from did:plc to did:web (or vice versa) while preserving their content and social connections would lower the barrier to experimentation.

Advanced DNS Configuration: Automating the DNS record creation through integration with popular DNS providers could eliminate one of the more technical steps.

Backup and Recovery Guidance: More comprehensive documentation about safely backing up and recovering did:web identities would help users understand the long-term implications of their choice.

The Experience: Smooth Sailing

The actual process of creating the account was remarkably smooth. From start to finish, it took maybe twenty minutes, and most of that was waiting for DNS propagation and double-checking that I'd uploaded the DID documents correctly.

The most nerve-wracking moment was the final activation step—there's always that brief pause where you wonder if everything will actually work. But Luke's tool had been so methodical about validation at each step that I was reasonably confident it would succeed. And it did.

Seeing @web.ewancroft.uk appear in the Bluesky interface, fully functional and indistinguishable from any other account, was genuinely satisfying. The DID document at ewancroft.uk/.well-known/did.json resolves correctly, the cryptographic verification works perfectly, and the account can interact normally with the rest of the ATProto ecosystem.

A Note on Security and Best Practices

Working with did:web identities does require taking security seriously. Your private key is your identity—lose it, and you lose access to your account permanently. Unlike traditional web services where you can reset passwords through email verification, cryptographic identity systems don't have that safety net.

Luke's tool handles the cryptographic operations correctly, but it's worth understanding what you're responsible for:

Private Key Storage: Keep multiple secure backups of your private key. Consider using a password manager or hardware security key for storage.

Domain Security: Your domain registrar and hosting provider now have a level of control over your digital identity. Choose reputable providers and enable all available security features.

DID Document Integrity: If someone gains access to your web hosting, they could potentially modify your DID document. Regular monitoring and backup of the document is sensible.

DNS Security: For custom domain handles, your DNS provider becomes part of your identity infrastructure. Consider using DNS providers that support advanced security features like DNSSEC.

The tool includes helpful reminders about these considerations, but ultimately, managing a did:web identity requires taking ownership of more infrastructure than the average social media account.

Comparing the Experience: did:web vs did:plc

Having now used both identity methods, the differences are subtle but significant:

Setup Complexity: Creating a did:plc account is essentially invisible—Bluesky handles everything. With did:web, you need to understand domains, DNS, and file hosting. Luke's tool makes this manageable, but there's definitely more involved.

Ongoing Maintenance: did:plc accounts require no maintenance beyond normal password security. did:web accounts need you to keep your domain registration current, maintain hosting, and ensure the DID document remains accessible.

Recovery Options: If you lose access to a did:plc account, Bluesky's support might be able to help (though this isn't guaranteed). With did:web, recovery depends entirely on your backup strategy and domain access.

Independence: This is where did:web shines. Your identity isn't tied to any particular organisation's continued existence or goodwill. As long as you maintain your domain, your identity persists.

Interoperability: Both methods work identically within the ATProto ecosystem, but did:web has the theoretical advantage of being usable with any system that understands web-based DIDs.

From a user experience perspective, once everything is set up, both methods work identically. The differences are entirely in the infrastructure and control model underlying your digital identity.

Why This Matters

Creating this proof of concept reinforced something I've been thinking about regarding digital identity and platform independence. While did:plc is perfectly functional for most users, having did:web as an option provides genuine choice about how you manage your online presence.

Ideally, I'd love to see both did:plc and did:web coexisting on the same DID document—having the convenience of the PLC system alongside the independence of web-based identity resolution. That would create a proper safety net: if Bluesky's infrastructure becomes unavailable, applications could fall back to the did:web resolution. If your domain hosting has issues, the did:plc method would still function perfectly. But that's not how things work right now, unfortunately.

For the moment, it's about having alternatives rather than true redundancy. The flexibility is what matters, even if we can't quite achieve the ideal hybrid approach yet.

The Broader Picture: Decentralisation in Practice

What excites me about tools like Luke's is how they make advanced concepts accessible. Decentralised identity shouldn't require a computer science degree to implement. When the barrier to entry is low enough, more people experiment, and more experimentation leads to better understanding of what works and what doesn't.

The did:web method represents a fascinating middle ground between traditional web infrastructure and cutting-edge decentralised protocols. It leverages existing web standards (HTTPS, DNS, JSON) while providing cryptographic verifiability and interoperability with modern decentralised systems.

This approach also demonstrates how the ATProto ecosystem can accommodate different trust models and infrastructure preferences. Some users want the convenience of managed infrastructure (did:plc), others prefer full control over their identity hosting (did:web), and some might want hybrid approaches that combine multiple methods. The protocol's flexibility in supporting these different models is genuinely impressive.

Code Deep Dive: What Makes Luke's Tool Work

Having spent time examining the source code, several implementation details stand out as particularly well-executed:

State Management: The tool uses SolidJS's reactive primitives effectively, with createSignal for form inputs and createEffect for derived state like the DID document generation. The step-by-step progression is managed cleanly without complex state machines.

Error Handling: Each network operation includes proper try-catch blocks with meaningful error messages. The tool doesn't just fail silently—it tells you exactly what went wrong and often suggests how to fix it.

Validation: Before allowing progression to the next step, the tool validates that the current step has been completed correctly. This prevents cascading failures and ensures users don't skip crucial configuration steps.

Client-Side Security: The cryptographic operations use established libraries (@atproto/crypto, uint8arrays) rather than homebrew implementations. This is crucial for security—cryptography is not an area where you want to reinvent the wheel.

Clean Architecture: The component structure is logical and maintainable. The main component handles the workflow logic, while utility functions manage the cryptographic operations and API interactions.

const keyPair = await Secp256k1Keypair.create({ exportable: true });
const privateKeyBytes = await keyPair.export();
const privateKeyHex = ui8.toString(privateKeyBytes, "hex");

This sort of clean, readable code makes the tool trustworthy in a way that's crucial for identity management applications.

Real-World Implications and Use Cases

While my proof of concept is primarily experimental, there are legitimate use cases where did:web could be preferable to did:plc:

Organisational Accounts: Companies or institutions that want complete control over their digital identity infrastructure might prefer hosting their own DID documents rather than relying on external services.

Long-Term Archival: For accounts intended to persist for decades, controlling your own identity infrastructure eliminates dependencies on specific organisations' continued existence.

Compliance Requirements: Some regulatory environments might require keeping identity infrastructure within specific jurisdictions or under direct organisational control.

Educational Purposes: Academic institutions studying decentralised identity could benefit from understanding multiple DID methods in practice.

Backup Strategies: Advanced users might want did:web as a backup identity method, even if they primarily use did:plc for daily activities.

Of course, these benefits come with increased responsibility. You're now responsible for domain renewal, hosting uptime, security updates, and backup procedures. It's not necessarily more complex, but it does require a different mindset about digital identity management.

The Experience: Smooth Sailing

The actual process of creating the account was remarkably smooth. From start to finish, it took maybe twenty minutes, and most of that was waiting for DNS propagation and double-checking that I'd uploaded the DID documents correctly.

The most nerve-wracking moment was the final activation step—there's always that brief pause where you wonder if everything will actually work. But Luke's tool had been so methodical about validation at each step that I was reasonably confident it would succeed. And it did.

Seeing @web.ewancroft.uk appear in the Bluesky interface, fully functional and indistinguishable from any other account, was genuinely satisfying. The DID document at ewancroft.uk/.well-known/did.json resolves correctly, the cryptographic verification works perfectly, and the account can interact normally with the rest of the ATProto ecosystem.

Future Possibilities and Protocol Evolution

Looking ahead, I'm curious about how did:web support might evolve within the ATProto ecosystem. There are several areas where improvements could make this approach even more compelling:

Automatic Synchronisation: Tools that automatically keep multiple DID methods in sync could reduce the maintenance burden while providing redundancy.

Enhanced PDS Support: More PDS implementations with first-class did:web support could expand the options for users who want this approach.

Migration Utilities: Safe methods for migrating between different DID methods, or for converting existing did:plc accounts to hybrid did:plc/did:web setups.

Improved Tooling: While Luke's tool is excellent, there's room for additional features like key rotation, bulk account creation, or integration with existing identity management systems.

Protocol Extensions: Future versions of the ATProto specification might include better support for multi-method identities or more sophisticated fallback mechanisms.

The foundation is solid, but there's definitely room for the ecosystem to grow around these alternative identity methods.

Reflections on Digital Sovereignty

This experiment reinforced my belief that genuine digital sovereignty requires understanding and controlling the infrastructure that supports your online presence. Running your own PDS, managing your own domain, and controlling your identity documents might seem like overkill for casual social media use, but they provide options that simply don't exist with traditional platforms.

The beauty of the ATProto approach is that it doesn't force this complexity on everyone. Casual users can stick with did:plc and never think about the underlying infrastructure, while technical users can dive as deep as they want into self-hosting and custom configurations. It's a rare example of a system that scales from "just works" to "completely customisable" without breaking at the boundaries.

Looking Forward

This experiment was primarily about understanding the technical implementation rather than establishing a permanent second identity. The @web.ewancroft.uk account will likely remain a dormant proof of concept, but the process taught me a lot about how different DID methods can coexist within the same ecosystem.

Luke's tool makes did:web accessible to anyone with basic technical skills and their own domain. That's significant for the broader adoption of decentralised identity standards. When the barrier to entry is low enough, more people experiment, and more experimentation leads to better understanding of what works and what doesn't.

The tool also serves as an excellent educational resource. By walking through each step of the process and showing exactly what's happening at each stage, it demystifies concepts that could otherwise seem intimidating. Whether you're implementing your own did:web identity or just trying to understand how decentralised identity works, the tool provides valuable insights.

If you're curious about trying this yourself, the tool is available at atproto-did-web.lukeacl.com . Just remember to keep your private key safe—treat it like a password, because that's essentially what it is.

The source code for Luke's tool demonstrates thoughtful engineering and a genuine commitment to making decentralised identity more accessible. Sometimes the best tools are the ones that make complex processes feel simple, and this definitely qualifies. It's exactly the sort of contribution that helps the broader ATProto ecosystem mature and become more welcoming to users with different technical backgrounds and infrastructure preferences.