Resolve AT Protocol DIDs, handles, and schemas with intelligent caching for Laravel
1[![Resolver Header](./header.png)](https://github.com/socialdept/atp-signals) 2 3<h3 align="center"> 4 Resolve AT Protocol identities in your Laravel application. 5</h3> 6 7<p align="center"> 8 <br> 9 <a href="https://packagist.org/packages/socialdept/atp-resolver" title="Latest Version on Packagist"><img src="https://img.shields.io/packagist/v/socialdept/atp-resolver.svg?style=flat-square"></a> 10 <a href="https://packagist.org/packages/socialdept/atp-resolver" title="Total Downloads"><img src="https://img.shields.io/packagist/dt/socialdept/atp-resolver.svg?style=flat-square"></a> 11 <a href="https://github.com/socialdept/atp-resolver/actions/workflows/tests.yml" title="GitHub Tests Action Status"><img src="https://img.shields.io/github/actions/workflow/status/socialdept/atp-resolver/tests.yml?branch=main&label=tests&style=flat-square"></a> 12 <a href="LICENSE" title="Software License"><img src="https://img.shields.io/github/license/socialdept/atp-resolver?style=flat-square"></a> 13</p> 14 15--- 16 17## What is Resolver? 18 19**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. 20 21Think of it as a Swiss Army knife for AT Protocol identity resolution. 22 23## Why use Resolver? 24 25- **Simple API** - Resolve DIDs and handles with one method call 26- **Automatic caching** - Smart caching with configurable TTLs 27- **Multiple DID methods** - Support for `did:plc` and `did:web` 28- **PDS discovery** - Find the correct PDS endpoint for any user 29- **Production ready** - Battle-tested with proper error handling 30- **Zero config** - Works out of the box with sensible defaults 31 32## Quick Example 33 34```php 35use SocialDept\AtpResolver\Facades\Resolver; 36 37// Resolve a DID to its document 38$document = Resolver::resolveDid('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); 39$handle = $document->getHandle(); // "user.bsky.social" 40$pds = $document->getPdsEndpoint(); // "https://bsky.social" 41 42// Convert a handle to its DID 43$did = Resolver::handleToDid('user.bsky.social'); 44// "did:plc:ewvi7nxzyoun6zhxrhs64oiz" 45 46// Resolve any identity (DID or handle) to a document 47$document = Resolver::resolveIdentity('alice.bsky.social'); 48 49// Find someone's PDS endpoint 50$pds = Resolver::resolvePds('alice.bsky.social'); 51// "https://bsky.social" 52``` 53 54## Installation 55 56```bash 57composer require socialdept/atp-resolver 58``` 59 60Resolver will auto-register with Laravel. Optionally publish the config: 61 62```bash 63php artisan vendor:publish --tag=resolver-config 64``` 65 66## Basic Usage 67 68### Resolving DIDs 69 70Resolver supports both `did:plc` and `did:web` methods: 71 72```php 73use SocialDept\AtpResolver\Facades\Resolver; 74 75// PLC directory resolution 76$document = Resolver::resolveDid('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); 77 78// Web DID resolution 79$document = Resolver::resolveDid('did:web:example.com'); 80 81// Access document data 82$handle = $document->getHandle(); 83$pdsEndpoint = $document->getPdsEndpoint(); 84$services = $document->service; 85``` 86 87### Resolving Handles 88 89Convert human-readable handles to DIDs or DID documents: 90 91```php 92// Convert handle to DID string 93$did = Resolver::handleToDid('alice.bsky.social'); 94// "did:plc:ewvi7nxzyoun6zhxrhs64oiz" 95 96// Resolve handle to full DID document 97$document = Resolver::resolveHandle('alice.bsky.social'); 98$handle = $document->getHandle(); 99$pds = $document->getPdsEndpoint(); 100``` 101 102### Resolving Identities 103 104Automatically detect and resolve either DIDs or handles: 105 106```php 107// Works with DIDs 108$document = Resolver::resolveIdentity('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); 109 110// Works with handles 111$document = Resolver::resolveIdentity('alice.bsky.social'); 112 113// Perfect for user input where type is unknown 114$actor = $request->input('actor'); // Could be DID or handle 115$document = Resolver::resolveIdentity($actor); 116``` 117 118### Finding PDS Endpoints 119 120Automatically discover a user's Personal Data Server: 121 122```php 123// From a DID 124$pds = Resolver::resolvePds('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); 125 126// From a handle 127$pds = Resolver::resolvePds('alice.bsky.social'); 128 129// Returns: "https://bsky.social" or user's custom PDS 130``` 131 132This is particularly useful when you need to make API calls to a user's PDS instead of hardcoding Bluesky's public instance. 133 134### Cache Management 135 136Beacon automatically caches resolutions. Clear the cache when needed: 137 138```php 139// Clear specific DID cache 140Resolver::clearDidCache('did:plc:abc123'); 141 142// Clear specific handle cache 143Resolver::clearHandleCache('alice.bsky.social'); 144 145// Clear specific PDS cache 146Resolver::clearPdsCache('alice.bsky.social'); 147 148// Clear all cached data 149Resolver::clearCache(); 150``` 151 152### Disable Caching 153 154Pass `false` as the second parameter to bypass cache: 155 156```php 157$document = Resolver::resolveDid('did:plc:abc123', useCache: false); 158$did = Resolver::handleToDid('alice.bsky.social', useCache: false); 159$document = Resolver::resolveIdentity('alice.bsky.social', useCache: false); 160$pds = Resolver::resolvePds('alice.bsky.social', useCache: false); 161``` 162 163### Identity Validation 164 165Beacon includes static helper methods to validate DIDs and handles: 166 167```php 168use SocialDept\AtpResolver\Support\Identity; 169 170// Validate handles 171Identity::isHandle('alice.bsky.social'); // true 172Identity::isHandle('invalid'); // false 173 174// Validate DIDs 175Identity::isDid('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); // true 176Identity::isDid('did:web:example.com'); // true 177Identity::isDid('invalid'); // false 178 179// Extract DID method 180Identity::extractDidMethod('did:plc:abc123'); // "plc" 181Identity::extractDidMethod('did:web:test'); // "web" 182 183// Check specific DID types 184Identity::isPlcDid('did:plc:abc123'); // true 185Identity::isWebDid('did:web:test'); // true 186``` 187 188These helpers are useful for validating user input before making resolution calls. 189 190## Configuration 191 192Beacon works great with zero configuration, but you can customize behavior in `config/resolver.php`: 193 194```php 195return [ 196 // PLC directory for did:plc resolution 197 'plc_directory' => env('RESOLVER_PLC_DIRECTORY', 'https://plc.directory'), 198 199 // Default PDS endpoint for handle resolution 200 'pds_endpoint' => env('RESOLVER_PDS_ENDPOINT', 'https://bsky.social'), 201 202 // HTTP request timeout 203 'timeout' => env('RESOLVER_TIMEOUT', 10), 204 205 // Cache configuration 206 'cache' => [ 207 'enabled' => env('RESOLVER_CACHE_ENABLED', true), 208 209 // Cache TTL for DID documents (1 hour) 210 'did_ttl' => env('RESOLVER_CACHE_DID_TTL', 3600), 211 212 // Cache TTL for handle resolutions (1 hour) 213 'handle_ttl' => env('RESOLVER_CACHE_HANDLE_TTL', 3600), 214 215 // Cache TTL for PDS endpoints (1 hour) 216 'pds_ttl' => env('RESOLVER_CACHE_PDS_TTL', 3600), 217 ], 218]; 219``` 220 221## API Reference 222 223### Available Methods 224 225```php 226// DID Resolution 227Resolver::resolveDid(string $did, bool $useCache = true): DidDocument 228 229// Handle Resolution 230Resolver::handleToDid(string $handle, bool $useCache = true): string 231Resolver::resolveHandle(string $handle, bool $useCache = true): DidDocument 232 233// Identity Resolution 234Resolver::resolveIdentity(string $actor, bool $useCache = true): DidDocument 235 236// PDS Resolution 237Resolver::resolvePds(string $actor, bool $useCache = true): ?string 238 239// Cache Management 240Resolver::clearDidCache(string $did): void 241Resolver::clearHandleCache(string $handle): void 242Resolver::clearPdsCache(string $actor): void 243Resolver::clearCache(): void 244 245// Identity Validation (static helpers) 246Identity::isHandle(?string $handle): bool 247Identity::isDid(?string $did): bool 248Identity::extractDidMethod(string $did): ?string 249Identity::isPlcDid(string $did): bool 250Identity::isWebDid(string $did): bool 251``` 252 253### DidDocument Object 254 255```php 256$document->id; // string - The DID 257$document->alsoKnownAs; // array - Alternative identifiers 258$document->verificationMethod; // array - Verification methods 259$document->service; // array - Service endpoints 260$document->raw; // array - Raw DID document 261 262// Helper methods 263$document->getHandle(); // ?string - Extract handle from alsoKnownAs 264$document->getPdsEndpoint(); // ?string - Extract PDS service endpoint 265$document->toArray(); // array - Convert to array 266``` 267 268## Error Handling 269 270Beacon throws descriptive exceptions when resolution fails: 271 272```php 273use SocialDept\AtpResolver\Exceptions\DidResolutionException; 274use SocialDept\AtpResolver\Exceptions\HandleResolutionException; 275 276try { 277 $document = Resolver::resolveDid('did:invalid:format'); 278} catch (DidResolutionException $e) { 279 // Handle DID resolution errors 280 logger()->error('DID resolution failed', [ 281 'message' => $e->getMessage(), 282 ]); 283} 284 285try { 286 $did = Resolver::handleToDid('invalid-handle'); 287} catch (HandleResolutionException $e) { 288 // Handle handle resolution errors 289} 290``` 291 292## Use Cases 293 294### Building an AppView 295 296```php 297// Resolve user identity from DID 298$document = Resolver::resolveDid($event->did); 299$handle = $document->getHandle(); 300 301// Make authenticated requests to their PDS 302$pds = Resolver::resolvePds($event->did); 303$client = new AtProtoClient($pds); 304``` 305 306### Custom Feed Generators 307 308```php 309// Resolve multiple handles efficiently (caching kicks in) 310$dids = collect(['alice.bsky.social', 'bob.bsky.social']) 311 ->map(fn($handle) => Resolver::handleToDid($handle)) 312 ->all(); 313``` 314 315### Profile Resolution 316 317```php 318// Get complete identity information 319$document = Resolver::resolveIdentity($username); 320 321$profile = [ 322 'did' => $document->id, 323 'handle' => $document->getHandle(), 324 'pds' => $document->getPdsEndpoint(), 325]; 326``` 327 328### Input Validation 329 330```php 331use SocialDept\AtpResolver\Support\Identity; 332use SocialDept\AtpResolver\Facades\Resolver; 333 334// Validate user input before resolving 335$actor = request()->input('actor'); 336 337if (Identity::isHandle($actor) || Identity::isDid($actor)) { 338 $document = Resolver::resolveIdentity($actor); 339} else { 340 abort(422, 'Invalid handle or DID'); 341} 342 343// Or convert handle to DID 344if (Identity::isHandle($actor)) { 345 $did = Resolver::handleToDid($actor); 346} 347``` 348 349## Requirements 350 351- PHP 8.2+ 352- Laravel 11+ 353- `ext-gmp` extension 354 355## Resources 356 357- [AT Protocol Documentation](https://atproto.com/) 358- [DID:PLC Specification](https://github.com/did-method-plc/did-method-plc) 359- [DID:Web Specification](https://w3c-ccg.github.io/did-method-web/) 360- [PLC Directory](https://plc.directory/) 361 362## Support & Contributing 363 364Found a bug or have a feature request? [Open an issue](https://github.com/socialdept/atp-resolver/issues). 365 366Want to contribute? We'd love your help! Check out the [contribution guidelines](CONTRIBUTING.md). 367 368## Credits 369 370- [Miguel Batres](https://batres.co) - founder & lead maintainer 371- [All contributors](https://github.com/socialdept/atp-resolver/graphs/contributors) 372 373## License 374 375Beacon is open-source software licensed under the [MIT license](LICENSE). 376 377--- 378 379**Built for the Federation** • By Social Dept.