socialdept / atp-resolver
Resolve AT Protocol DIDs and handles in Laravel
Installs: 107
Dependents: 2
Suggesters: 1
Security: 0
Stars: 2
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/socialdept/atp-resolver
Requires
- php: ^8.2
- ext-gmp: *
- guzzlehttp/guzzle: ^7.5
- illuminate/cache: ^11.0|^12.0
- illuminate/console: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.89
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^11.0
README
Resolve AT Protocol identities in your Laravel application.
What is Resolver?
Resolver is a Laravel package that resolves AT Protocol identities. Convert DIDs to handles, find PDS endpoints, and resolve DID documents with automatic caching and fallback support for both did:plc and did:web methods.
Think of it as a Swiss Army knife for AT Protocol identity resolution.
Why use Resolver?
- Simple API - Resolve DIDs and handles with one method call
- Automatic caching - Smart caching with configurable TTLs
- Multiple DID methods - Support for
did:plcanddid:web - PDS discovery - Find the correct PDS endpoint for any user
- Production ready - Battle-tested with proper error handling
- Zero config - Works out of the box with sensible defaults
Quick Example
use SocialDept\AtpResolver\Facades\Resolver; // Resolve a DID to its document $document = Resolver::resolveDid('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); $handle = $document->getHandle(); // "user.bsky.social" $pds = $document->getPdsEndpoint(); // "https://bsky.social" // Convert a handle to its DID $did = Resolver::handleToDid('user.bsky.social'); // "did:plc:ewvi7nxzyoun6zhxrhs64oiz" // Resolve any identity (DID or handle) to a document $document = Resolver::resolveIdentity('alice.bsky.social'); // Find someone's PDS endpoint $pds = Resolver::resolvePds('alice.bsky.social'); // "https://bsky.social"
Installation
composer require socialdept/atp-resolver
Resolver will auto-register with Laravel. Optionally publish the config:
php artisan vendor:publish --tag=resolver-config
Basic Usage
Resolving DIDs
Resolver supports both did:plc and did:web methods:
use SocialDept\AtpResolver\Facades\Resolver; // PLC directory resolution $document = Resolver::resolveDid('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); // Web DID resolution $document = Resolver::resolveDid('did:web:example.com'); // Access document data $handle = $document->getHandle(); $pdsEndpoint = $document->getPdsEndpoint(); $services = $document->service;
Resolving Handles
Convert human-readable handles to DIDs or DID documents:
// Convert handle to DID string $did = Resolver::handleToDid('alice.bsky.social'); // "did:plc:ewvi7nxzyoun6zhxrhs64oiz" // Resolve handle to full DID document $document = Resolver::resolveHandle('alice.bsky.social'); $handle = $document->getHandle(); $pds = $document->getPdsEndpoint();
Resolving Identities
Automatically detect and resolve either DIDs or handles:
// Works with DIDs $document = Resolver::resolveIdentity('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); // Works with handles $document = Resolver::resolveIdentity('alice.bsky.social'); // Perfect for user input where type is unknown $actor = $request->input('actor'); // Could be DID or handle $document = Resolver::resolveIdentity($actor);
Finding PDS Endpoints
Automatically discover a user's Personal Data Server:
// From a DID $pds = Resolver::resolvePds('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); // From a handle $pds = Resolver::resolvePds('alice.bsky.social'); // Returns: "https://bsky.social" or user's custom PDS
This is particularly useful when you need to make API calls to a user's PDS instead of hardcoding Bluesky's public instance.
Cache Management
Beacon automatically caches resolutions. Clear the cache when needed:
// Clear specific DID cache Resolver::clearDidCache('did:plc:abc123'); // Clear specific handle cache Resolver::clearHandleCache('alice.bsky.social'); // Clear specific PDS cache Resolver::clearPdsCache('alice.bsky.social'); // Clear all cached data Resolver::clearCache();
Disable Caching
Pass false as the second parameter to bypass cache:
$document = Resolver::resolveDid('did:plc:abc123', useCache: false); $did = Resolver::handleToDid('alice.bsky.social', useCache: false); $document = Resolver::resolveIdentity('alice.bsky.social', useCache: false); $pds = Resolver::resolvePds('alice.bsky.social', useCache: false);
Identity Validation
Beacon includes static helper methods to validate DIDs and handles:
use SocialDept\AtpResolver\Support\Identity; // Validate handles Identity::isHandle('alice.bsky.social'); // true Identity::isHandle('invalid'); // false // Validate DIDs Identity::isDid('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); // true Identity::isDid('did:web:example.com'); // true Identity::isDid('invalid'); // false // Extract DID method Identity::extractDidMethod('did:plc:abc123'); // "plc" Identity::extractDidMethod('did:web:test'); // "web" // Check specific DID types Identity::isPlcDid('did:plc:abc123'); // true Identity::isWebDid('did:web:test'); // true
These helpers are useful for validating user input before making resolution calls.
Configuration
Beacon works great with zero configuration, but you can customize behavior in config/resolver.php:
return [ // PLC directory for did:plc resolution 'plc_directory' => env('RESOLVER_PLC_DIRECTORY', 'https://plc.directory'), // Default PDS endpoint for handle resolution 'pds_endpoint' => env('RESOLVER_PDS_ENDPOINT', 'https://bsky.social'), // HTTP request timeout 'timeout' => env('RESOLVER_TIMEOUT', 10), // Cache configuration 'cache' => [ 'enabled' => env('RESOLVER_CACHE_ENABLED', true), // Cache TTL for DID documents (1 hour) 'did_ttl' => env('RESOLVER_CACHE_DID_TTL', 3600), // Cache TTL for handle resolutions (1 hour) 'handle_ttl' => env('RESOLVER_CACHE_HANDLE_TTL', 3600), // Cache TTL for PDS endpoints (1 hour) 'pds_ttl' => env('RESOLVER_CACHE_PDS_TTL', 3600), ], ];
API Reference
Available Methods
// DID Resolution Resolver::resolveDid(string $did, bool $useCache = true): DidDocument // Handle Resolution Resolver::handleToDid(string $handle, bool $useCache = true): string Resolver::resolveHandle(string $handle, bool $useCache = true): DidDocument // Identity Resolution Resolver::resolveIdentity(string $actor, bool $useCache = true): DidDocument // PDS Resolution Resolver::resolvePds(string $actor, bool $useCache = true): ?string // Cache Management Resolver::clearDidCache(string $did): void Resolver::clearHandleCache(string $handle): void Resolver::clearPdsCache(string $actor): void Resolver::clearCache(): void // Identity Validation (static helpers) Identity::isHandle(?string $handle): bool Identity::isDid(?string $did): bool Identity::extractDidMethod(string $did): ?string Identity::isPlcDid(string $did): bool Identity::isWebDid(string $did): bool
DidDocument Object
$document->id; // string - The DID $document->alsoKnownAs; // array - Alternative identifiers $document->verificationMethod; // array - Verification methods $document->service; // array - Service endpoints $document->raw; // array - Raw DID document // Helper methods $document->getHandle(); // ?string - Extract handle from alsoKnownAs $document->getPdsEndpoint(); // ?string - Extract PDS service endpoint $document->toArray(); // array - Convert to array
Error Handling
Beacon throws descriptive exceptions when resolution fails:
use SocialDept\AtpResolver\Exceptions\DidResolutionException; use SocialDept\AtpResolver\Exceptions\HandleResolutionException; try { $document = Resolver::resolveDid('did:invalid:format'); } catch (DidResolutionException $e) { // Handle DID resolution errors logger()->error('DID resolution failed', [ 'message' => $e->getMessage(), ]); } try { $did = Resolver::handleToDid('invalid-handle'); } catch (HandleResolutionException $e) { // Handle handle resolution errors }
Use Cases
Building an AppView
// Resolve user identity from DID $document = Resolver::resolveDid($event->did); $handle = $document->getHandle(); // Make authenticated requests to their PDS $pds = Resolver::resolvePds($event->did); $client = new AtProtoClient($pds);
Custom Feed Generators
// Resolve multiple handles efficiently (caching kicks in) $dids = collect(['alice.bsky.social', 'bob.bsky.social']) ->map(fn($handle) => Resolver::handleToDid($handle)) ->all();
Profile Resolution
// Get complete identity information $document = Resolver::resolveIdentity($username); $profile = [ 'did' => $document->id, 'handle' => $document->getHandle(), 'pds' => $document->getPdsEndpoint(), ];
Input Validation
use SocialDept\AtpResolver\Support\Identity; use SocialDept\AtpResolver\Facades\Resolver; // Validate user input before resolving $actor = request()->input('actor'); if (Identity::isHandle($actor) || Identity::isDid($actor)) { $document = Resolver::resolveIdentity($actor); } else { abort(422, 'Invalid handle or DID'); } // Or convert handle to DID if (Identity::isHandle($actor)) { $did = Resolver::handleToDid($actor); }
Requirements
- PHP 8.2+
- Laravel 11+
ext-gmpextension
Resources
Support & Contributing
Found a bug or have a feature request? Open an issue.
Want to contribute? We'd love your help! Check out the contribution guidelines.
Credits
- Miguel Batres - founder & lead maintainer
- All contributors
License
Beacon is open-source software licensed under the MIT license.
Built for the Federation • By Social Dept.
