socialdept / atp-support
Foundational utilities for AT Protocol packages in Laravel
Installs: 5
Dependents: 4
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/socialdept/atp-support
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.5
- illuminate/cache: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.89
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^11.0
README
Resolve DIDs, handles, and identities for AT Protocol in Laravel.
What is ATP Support?
ATP Support is the foundational Laravel package for the SocialDept AT Protocol ecosystem. It provides DID and handle resolution, identity validation, AT-URI parsing, NSID utilities, and shared configuration used by all other atp-* packages.
If you're building anything on AT Protocol with Laravel, this is your starting point.
Why use ATP Support?
- Identity resolution - Resolve DIDs, handles, and PDS endpoints with intelligent caching
- Validation utilities - Validate DIDs, handles, and NSIDs with battle-tested logic
- AT-URI parsing - Parse and create
at://URIs as immutable value objects - Shared configuration - Common AT Protocol settings (PLC directory, PDS endpoint, public API) in one place
- Extensible resolvers - Pluggable DID method resolvers with support for
did:plcanddid:web - DNS lexicon resolution - Discover lexicon schemas via DNS TXT records
Quick Example
use SocialDept\AtpSupport\Facades\Resolver; use SocialDept\AtpSupport\Identity; use SocialDept\AtpSupport\AtUri; use SocialDept\AtpSupport\Nsid; // Resolve a handle to a DID $did = Resolver::handleToDid('alice.bsky.social'); // Resolve a DID to its PDS endpoint $pds = Resolver::resolvePds('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); // Validate identities Identity::isDid('did:plc:abc123'); // true Identity::isHandle('alice.bsky.social'); // true // Parse AT-URIs $uri = AtUri::parse('at://did:plc:xyz/app.bsky.feed.post/3k4abc'); $uri->did; // did:plc:xyz $uri->collection; // app.bsky.feed.post $uri->rkey; // 3k4abc // Work with NSIDs $nsid = Nsid::parse('app.bsky.feed.post'); $nsid->getAuthority(); // app.bsky.feed $nsid->getName(); // post
Installation
composer require socialdept/atp-support
Publish the configuration:
php artisan vendor:publish --tag=atp-support-config
Configuration
All shared AT Protocol settings live in config/atp-support.php:
return [ 'plc_directory' => env('ATP_PLC_DIRECTORY', 'https://plc.directory'), 'pds_endpoint' => env('ATP_PDS_ENDPOINT', 'https://bsky.social'), 'public_api' => env('ATP_PUBLIC_API', 'https://public.api.bsky.app'), 'timeout' => env('ATP_RESOLVER_TIMEOUT', 10), 'cache' => [ 'enabled' => env('ATP_RESOLVER_CACHE_ENABLED', true), 'did_ttl' => env('ATP_RESOLVER_CACHE_DID_TTL', 3600), 'handle_ttl' => env('ATP_RESOLVER_CACHE_HANDLE_TTL', 3600), 'pds_ttl' => env('ATP_RESOLVER_CACHE_PDS_TTL', 3600), ], ];
Other atp-* packages read shared values like public_api directly from this config.
Usage
Resolving Identities
The Resolver facade is the main entry point for all resolution operations:
use SocialDept\AtpSupport\Facades\Resolver; // Resolve a DID to its DID Document $doc = Resolver::resolveDid('did:plc:ewvi7nxzyoun6zhxrhs64oiz'); $doc->getPdsEndpoint(); // https://morel.us-east.host.bsky.network $doc->getHandle(); // alice.bsky.social // Convert a handle to a DID $did = Resolver::handleToDid('alice.bsky.social'); // Auto-detect and resolve (accepts both DIDs and handles) $doc = Resolver::resolveIdentity('alice.bsky.social'); // Resolve directly to PDS endpoint $pds = Resolver::resolvePds('alice.bsky.social');
Caching
All resolutions are cached by default with configurable TTLs. You can bypass or clear the cache:
// Bypass cache for a single call $doc = Resolver::resolveDid('did:plc:abc123', useCache: false); // Clear specific caches Resolver::clearDidCache('did:plc:abc123'); Resolver::clearHandleCache('alice.bsky.social'); Resolver::clearPdsCache('did:plc:abc123'); // Clear everything Resolver::clearCache();
Validating Identities
The Identity class provides static validation methods:
use SocialDept\AtpSupport\Identity; Identity::isDid('did:plc:abc123'); // true Identity::isDid('not-a-did'); // false Identity::isHandle('alice.bsky.social'); // true Identity::isHandle('invalid'); // false Identity::isPlcDid('did:plc:abc123'); // true Identity::isWebDid('did:web:example.com'); // true Identity::extractDidMethod('did:plc:abc123'); // "plc"
Parsing AT-URIs
The AtUri value object parses the at:// URI format used throughout AT Protocol:
use SocialDept\AtpSupport\AtUri; $uri = AtUri::parse('at://did:plc:xyz/app.bsky.feed.post/3k4abc'); $uri->did; // did:plc:xyz $uri->collection; // app.bsky.feed.post $uri->rkey; // 3k4abc // Create programmatically $uri = AtUri::make('did:plc:xyz', 'app.bsky.feed.post', '3k4abc'); echo $uri; // at://did:plc:xyz/app.bsky.feed.post/3k4abc // Returns null for invalid URIs AtUri::parse('not-a-uri'); // null
Working with NSIDs
Namespace Identifiers are the reversed-domain notation used for AT Protocol collections and methods:
use SocialDept\AtpSupport\Nsid; $nsid = Nsid::parse('app.bsky.feed.post'); $nsid->getAuthority(); // app.bsky.feed $nsid->getName(); // post $nsid->getSegments(); // ['app', 'bsky', 'feed', 'post'] $nsid->toDomain(); // post.feed.bsky.app $nsid->getAuthorityDomain(); // feed.bsky.app // Validation Nsid::isValid('app.bsky.feed.post'); // true Nsid::isValid('invalid'); // false // Equality $nsid->equals(Nsid::parse('app.bsky.feed.post')); // true
DNS Lexicon Resolution
Discover lexicon schemas published via DNS TXT records:
use SocialDept\AtpSupport\Resolvers\LexiconDnsResolver; $resolver = app(LexiconDnsResolver::class); // Full pipeline: DNS lookup -> DID resolution -> XRPC fetch $schema = $resolver->resolve('com.example.myrecord'); // Individual steps $did = $resolver->lookupDns('example.com'); $schema = $resolver->retrieveSchema($pdsEndpoint, $did, 'com.example.myrecord');
Custom DID Resolvers
Register custom resolvers for additional DID methods:
use SocialDept\AtpSupport\Contracts\DidResolver; use SocialDept\AtpSupport\Data\DidDocument; class CustomDidResolver implements DidResolver { public function resolve(string $did): DidDocument { // Your resolution logic } public function supports(string $method): bool { return $method === 'custom'; } } // Register in a service provider $manager = app(DidResolverManager::class); $manager->register('custom', new CustomDidResolver());
API Reference
Facade Methods
| Method | Description |
|---|---|
Resolver::resolveDid($did) |
Resolve DID to DidDocument |
Resolver::handleToDid($handle) |
Convert handle to DID string |
Resolver::resolveHandle($handle) |
Resolve handle to DidDocument |
Resolver::resolveIdentity($actor) |
Auto-detect and resolve DID or handle |
Resolver::resolvePds($actor) |
Get PDS endpoint for DID or handle |
Resolver::clearDidCache($did) |
Clear cached DID data |
Resolver::clearHandleCache($handle) |
Clear cached handle data |
Resolver::clearPdsCache($actor) |
Clear cached PDS data |
Resolver::clearCache() |
Clear all cached data |
Value Objects
| Class | Description |
|---|---|
AtUri |
Immutable AT-URI parser (at://did/collection/rkey) |
Nsid |
Immutable Namespace Identifier |
DidDocument |
Resolved DID Document with PDS and handle access |
Validation
| Method | Description |
|---|---|
Identity::isDid($value) |
Validate DID format |
Identity::isHandle($value) |
Validate handle format |
Identity::isPlcDid($did) |
Check for did:plc method |
Identity::isWebDid($did) |
Check for did:web method |
Identity::extractDidMethod($did) |
Get method from DID string |
Nsid::isValid($nsid) |
Validate NSID format |
Exceptions
| Exception | Description |
|---|---|
ResolverException |
Base exception for all resolution errors |
DidResolutionException |
DID resolution failures |
HandleResolutionException |
Handle resolution failures |
Requirements
- PHP 8.2+
- Laravel 11+
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
ATP Support is open-source software licensed under the MIT license.
Built for the Atmosphere • By Social Dept.